resolve cyclic dependency with zstd
[platform/upstream/cmake.git] / Source / cmGlobalVisualStudio10Generator.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 "cmGlobalVisualStudio10Generator.h"
4
5 #include <algorithm>
6 #include <cstring>
7 #include <map>
8 #include <sstream>
9 #include <utility>
10
11 #include <cm/memory>
12
13 #include <cm3p/json/reader.h>
14 #include <cm3p/json/value.h>
15
16 #include "cmsys/FStream.hxx"
17 #include "cmsys/Glob.hxx"
18 #include "cmsys/RegularExpression.hxx"
19
20 #include "cmDocumentationEntry.h"
21 #include "cmGeneratorTarget.h"
22 #include "cmGlobalGenerator.h"
23 #include "cmGlobalVisualStudio71Generator.h"
24 #include "cmGlobalVisualStudio7Generator.h"
25 #include "cmGlobalVisualStudioGenerator.h"
26 #include "cmIDEFlagTable.h"
27 #include "cmLocalGenerator.h"
28 #include "cmLocalVisualStudio10Generator.h"
29 #include "cmMakefile.h"
30 #include "cmMessageType.h"
31 #include "cmSourceFile.h"
32 #include "cmStringAlgorithms.h"
33 #include "cmSystemTools.h"
34 #include "cmVersion.h"
35 #include "cmVisualStudioSlnData.h"
36 #include "cmVisualStudioSlnParser.h"
37 #include "cmXMLWriter.h"
38 #include "cmake.h"
39
40 static std::map<std::string, std::vector<cmIDEFlagTable>> loadedFlagJsonFiles;
41
42 static void ConvertToWindowsSlashes(std::string& s)
43 {
44   // first convert all of the slashes
45   for (auto& ch : s) {
46     if (ch == '/') {
47       ch = '\\';
48     }
49   }
50 }
51
52 cmGlobalVisualStudio10Generator::cmGlobalVisualStudio10Generator(
53   cmake* cm, const std::string& name,
54   std::string const& platformInGeneratorName)
55   : cmGlobalVisualStudio8Generator(cm, name, platformInGeneratorName)
56 {
57   this->DefaultCudaFlagTableName = "v10";
58   this->DefaultCudaHostFlagTableName = "v10";
59   this->DefaultNasmFlagTableName = "v10";
60 }
61
62 bool cmGlobalVisualStudio10Generator::SetSystemName(std::string const& s,
63                                                     cmMakefile* mf)
64 {
65   this->SystemName = s;
66   this->SystemVersion = mf->GetSafeDefinition("CMAKE_SYSTEM_VERSION");
67   if (!this->InitializeSystem(mf)) {
68     return false;
69   }
70   return this->cmGlobalVisualStudio8Generator::SetSystemName(s, mf);
71 }
72
73 static void cmCudaToolVersion(std::string& s)
74 {
75   // "CUDA x.y.props" => "x.y"
76   s = s.substr(5);
77   s = s.substr(0, s.size() - 6);
78 }
79
80 bool cmGlobalVisualStudio10Generator::SetGeneratorToolset(
81   std::string const& ts, bool build, cmMakefile* mf)
82 {
83   if (this->SystemIsWindowsCE && ts.empty() &&
84       this->DefaultPlatformToolset.empty()) {
85     std::ostringstream e;
86     e << this->GetName() << " Windows CE version '" << this->SystemVersion
87       << "' requires CMAKE_GENERATOR_TOOLSET to be set.";
88     mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
89     return false;
90   }
91
92   if (!this->ParseGeneratorToolset(ts, mf)) {
93     return false;
94   }
95
96   if (build) {
97     return true;
98   }
99
100   if (this->CustomVCTargetsPath.empty() && !this->FindVCTargetsPath(mf)) {
101     return false;
102   }
103
104   if (!this->CustomFlagTableDir.empty() &&
105       !(cmSystemTools::FileIsFullPath(this->CustomFlagTableDir) &&
106         cmSystemTools::FileIsDirectory(this->CustomFlagTableDir))) {
107     std::ostringstream e;
108     /* clang-format off */
109     e <<
110       "Generator\n"
111       "  " << this->GetName() << "\n"
112       "given toolset\n"
113       "  customFlagTableDir=" << this->CustomFlagTableDir << "\n"
114       "that is not an absolute path to an existing directory.";
115     /* clang-format on */
116     mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
117     cmSystemTools::SetFatalErrorOccurred();
118     return false;
119   }
120
121   if (cmHasLiteralPrefix(this->GetPlatformToolsetString(), "v140")) {
122     // The GenerateDebugInformation link setting for the v140 toolset
123     // in VS 2015 was originally an enum with "No" and "Debug" values,
124     // differing from the "false" and "true" values used in older toolsets.
125     // A VS 2015 update changed it back.  Parse the "link.xml" file to
126     // discover which one we need.
127     std::string const link_xml = this->VCTargetsPath + "/1033/link.xml";
128     cmsys::ifstream fin(link_xml.c_str());
129     std::string line;
130     while (fin && cmSystemTools::GetLineFromStream(fin, line)) {
131       if (line.find(" Switch=\"DEBUG\" ") != std::string::npos) {
132         this->PlatformToolsetNeedsDebugEnum =
133           line.find(" Name=\"Debug\" ") != std::string::npos;
134         break;
135       }
136     }
137   }
138
139   this->SupportsUnityBuilds =
140     this->Version >= cmGlobalVisualStudioGenerator::VSVersion::VS16 ||
141     (this->Version == cmGlobalVisualStudioGenerator::VSVersion::VS15 &&
142      cmSystemTools::PathExists(this->VCTargetsPath +
143                                "/Microsoft.Cpp.Unity.targets"));
144
145   if (this->GeneratorToolsetCuda.empty()) {
146     // Find the highest available version of the CUDA tools.
147     std::vector<std::string> cudaTools;
148     std::string bcDir;
149     if (this->GeneratorToolsetCudaCustomDir.empty()) {
150       bcDir = this->VCTargetsPath + "/BuildCustomizations";
151     } else {
152       bcDir = this->GetPlatformToolsetCudaCustomDirString() +
153         this->GetPlatformToolsetCudaVSIntegrationSubdirString() +
154         "extras\\visual_studio_integration\\MSBuildExtensions";
155       cmSystemTools::ConvertToUnixSlashes(bcDir);
156     }
157     cmsys::Glob gl;
158     gl.SetRelative(bcDir.c_str());
159     if (gl.FindFiles(bcDir + "/CUDA *.props")) {
160       cudaTools = gl.GetFiles();
161     }
162     if (!cudaTools.empty()) {
163       std::for_each(cudaTools.begin(), cudaTools.end(), cmCudaToolVersion);
164       std::sort(cudaTools.begin(), cudaTools.end(),
165                 cmSystemTools::VersionCompareGreater);
166       this->GeneratorToolsetCuda = cudaTools.at(0);
167     } else if (!this->GeneratorToolsetCudaCustomDir.empty()) {
168       // Generate an error if Visual Studio integration files are not found
169       // inside of custom cuda toolset.
170       std::ostringstream e;
171       /* clang-format off */
172       e <<
173         "Generator\n"
174         "  " << this->GetName() << "\n"
175         "given toolset\n"
176         "  cuda=" << this->GeneratorToolsetCudaCustomDir << "\n"
177         "cannot detect Visual Studio integration files in path\n"
178         "  " << bcDir;
179
180       /* clang-format on */
181       mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
182
183       // Clear the configured tool-set
184       this->GeneratorToolsetCuda.clear();
185     }
186   }
187
188   if (!this->GeneratorToolsetVersion.empty() &&
189       this->GeneratorToolsetVersion != "Test Toolset Version") {
190     // If a specific minor version of the toolset was requested, verify that it
191     // is compatible to the major version and that is exists on disk.
192     // If not clear the value.
193     std::string versionToolset = this->GeneratorToolsetVersion;
194     cmsys::RegularExpression regex("[0-9][0-9]\\.[0-9][0-9]");
195     if (regex.find(versionToolset)) {
196       versionToolset = "v" + versionToolset.erase(2, 1);
197     } else {
198       // Version not recognized. Clear it.
199       versionToolset.clear();
200     }
201
202     if (!cmHasPrefix(versionToolset, this->GetPlatformToolsetString())) {
203       std::ostringstream e;
204       /* clang-format off */
205       e <<
206         "Generator\n"
207         "  " << this->GetName() << "\n"
208         "given toolset and version specification\n"
209         "  " << this->GetPlatformToolsetString() << ",version=" <<
210         this->GeneratorToolsetVersion << "\n"
211         "contains an invalid version specification."
212       ;
213       /* clang-format on */
214       mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
215
216       // Clear the configured tool-set
217       this->GeneratorToolsetVersion.clear();
218     }
219
220     std::string auxProps;
221     switch (this->FindAuxToolset(this->GeneratorToolsetVersion, auxProps)) {
222       case AuxToolset::None:
223         this->GeneratorToolsetVersionProps = {};
224         break;
225       case AuxToolset::Default:
226         // The given version is the default toolset.  Remove the setting.
227         this->GeneratorToolsetVersion.clear();
228         this->GeneratorToolsetVersionProps = {};
229         break;
230       case AuxToolset::PropsExist:
231         this->GeneratorToolsetVersionProps = std::move(auxProps);
232         break;
233       case AuxToolset::PropsMissing: {
234         std::ostringstream e;
235         /* clang-format off */
236         e <<
237           "Generator\n"
238           "  " << this->GetName() << "\n"
239           "given toolset and version specification\n"
240           "  " << this->GetPlatformToolsetString() << ",version=" <<
241           this->GeneratorToolsetVersion << "\n"
242           "does not seem to be installed at\n" <<
243           "  " << auxProps;
244         ;
245         /* clang-format on */
246         mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
247
248         // Clear the configured tool-set
249         this->GeneratorToolsetVersion.clear();
250         this->GeneratorToolsetVersionProps = {};
251       } break;
252       case AuxToolset::PropsIndeterminate: {
253         std::ostringstream e;
254         /* clang-format off */
255         e <<
256           "Generator\n"
257           "  " << this->GetName() << "\n"
258           "given toolset and version specification\n"
259           "  " << this->GetPlatformToolsetString() << ",version=" <<
260           this->GeneratorToolsetVersion << "\n"
261           "has multiple matches installed at\n" <<
262           "  " << auxProps << "\n" <<
263           "The toolset and version specification must resolve \n" <<
264            "to a single installed toolset";
265         ;
266         /* clang-format on */
267         mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
268
269         // Clear the configured tool-set
270         this->GeneratorToolsetVersion.clear();
271         this->GeneratorToolsetVersionProps = {};
272       } break;
273     }
274   }
275
276   if (const char* toolset = this->GetPlatformToolset()) {
277     mf->AddDefinition("CMAKE_VS_PLATFORM_TOOLSET", toolset);
278   }
279   if (!this->GeneratorToolsetVersion.empty()) {
280     mf->AddDefinition("CMAKE_VS_PLATFORM_TOOLSET_VERSION",
281                       this->GeneratorToolsetVersion);
282   }
283   if (const char* hostArch = this->GetPlatformToolsetHostArchitecture()) {
284     mf->AddDefinition("CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE", hostArch);
285   }
286   if (const char* cuda = this->GetPlatformToolsetCuda()) {
287     mf->AddDefinition("CMAKE_VS_PLATFORM_TOOLSET_CUDA", cuda);
288   }
289   if (const char* cudaDir = this->GetPlatformToolsetCudaCustomDir()) {
290     mf->AddDefinition("CMAKE_VS_PLATFORM_TOOLSET_CUDA_CUSTOM_DIR", cudaDir);
291   }
292   if (const char* vcTargetsDir = this->GetCustomVCTargetsPath()) {
293     mf->AddDefinition("CMAKE_VS_PLATFORM_TOOLSET_VCTARGETS_CUSTOM_DIR",
294                       vcTargetsDir);
295   }
296
297   return true;
298 }
299
300 bool cmGlobalVisualStudio10Generator::ParseGeneratorToolset(
301   std::string const& ts, cmMakefile* mf)
302 {
303   std::vector<std::string> const fields = cmTokenize(ts, ",");
304   std::vector<std::string>::const_iterator fi = fields.begin();
305   if (fi == fields.end()) {
306     return true;
307   }
308
309   // The first field may be the VS platform toolset.
310   if (fi->find('=') == fi->npos) {
311     this->GeneratorToolset = *fi;
312     ++fi;
313   }
314
315   std::set<std::string> handled;
316
317   // The rest of the fields must be key=value pairs.
318   for (; fi != fields.end(); ++fi) {
319     std::string::size_type pos = fi->find('=');
320     if (pos == fi->npos) {
321       std::ostringstream e;
322       /* clang-format off */
323       e <<
324         "Generator\n"
325         "  " << this->GetName() << "\n"
326         "given toolset specification\n"
327         "  " << ts << "\n"
328         "that contains a field after the first ',' with no '='."
329         ;
330       /* clang-format on */
331       mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
332       return false;
333     }
334     std::string const key = fi->substr(0, pos);
335     std::string const value = fi->substr(pos + 1);
336     if (!handled.insert(key).second) {
337       std::ostringstream e;
338       /* clang-format off */
339       e <<
340         "Generator\n"
341         "  " << this->GetName() << "\n"
342         "given toolset specification\n"
343         "  " << ts << "\n"
344         "that contains duplicate field key '" << key << "'."
345         ;
346       /* clang-format on */
347       mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
348       return false;
349     }
350     if (!this->ProcessGeneratorToolsetField(key, value)) {
351       std::ostringstream e;
352       /* clang-format off */
353       e <<
354         "Generator\n"
355         "  " << this->GetName() << "\n"
356         "given toolset specification\n"
357         "  " << ts << "\n"
358         "that contains invalid field '" << *fi << "'."
359         ;
360       /* clang-format on */
361       mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
362       return false;
363     }
364   }
365
366   return true;
367 }
368
369 bool cmGlobalVisualStudio10Generator::ProcessGeneratorToolsetField(
370   std::string const& key, std::string const& value)
371 {
372   if (key == "cuda") {
373     /* test if cuda toolset is path to custom dir or cuda version */
374     auto pos = value.find_first_not_of("0123456789.");
375     if (pos != std::string::npos) {
376       this->GeneratorToolsetCudaCustomDir = value;
377       /* ensure trailing backslash for easy path joining */
378       if (this->GeneratorToolsetCudaCustomDir.back() != '\\') {
379         this->GeneratorToolsetCudaCustomDir.push_back('\\');
380       }
381       /* check for legacy toolkit folder structure */
382       if (cmsys::SystemTools::FileIsDirectory(
383             cmStrCat(this->GeneratorToolsetCudaCustomDir, "nvcc"))) {
384         this->GeneratorToolsetCudaNvccSubdir = "nvcc\\";
385       }
386       if (cmsys::SystemTools::FileIsDirectory(
387             cmStrCat(this->GeneratorToolsetCudaCustomDir,
388                      "CUDAVisualStudioIntegration"))) {
389         this->GeneratorToolsetCudaVSIntegrationSubdir =
390           "CUDAVisualStudioIntegration\\";
391       }
392     } else {
393       this->GeneratorToolsetCuda = value;
394     }
395     return true;
396   }
397   if (key == "customFlagTableDir") {
398     this->CustomFlagTableDir = value;
399     cmSystemTools::ConvertToUnixSlashes(this->CustomFlagTableDir);
400     return true;
401   }
402   if (key == "version") {
403     this->GeneratorToolsetVersion = value;
404     return true;
405   }
406   if (key == "VCTargetsPath") {
407     this->CustomVCTargetsPath = value;
408     ConvertToWindowsSlashes(this->CustomVCTargetsPath);
409     return true;
410   }
411   return false;
412 }
413
414 bool cmGlobalVisualStudio10Generator::InitializeSystem(cmMakefile* mf)
415 {
416   if (this->SystemName == "Windows") {
417     if (!this->InitializeWindows(mf)) {
418       return false;
419     }
420   } else if (this->SystemName == "WindowsCE") {
421     this->SystemIsWindowsCE = true;
422     if (!this->InitializeWindowsCE(mf)) {
423       return false;
424     }
425   } else if (this->SystemName == "WindowsPhone") {
426     this->SystemIsWindowsPhone = true;
427     if (!this->InitializeWindowsPhone(mf)) {
428       return false;
429     }
430   } else if (this->SystemName == "WindowsStore") {
431     this->SystemIsWindowsStore = true;
432     if (!this->InitializeWindowsStore(mf)) {
433       return false;
434     }
435   } else if (this->SystemName == "Android") {
436     if (this->PlatformInGeneratorName) {
437       std::ostringstream e;
438       e << "CMAKE_SYSTEM_NAME is 'Android' but CMAKE_GENERATOR "
439         << "specifies a platform too: '" << this->GetName() << "'";
440       mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
441       return false;
442     }
443     if (mf->GetSafeDefinition("CMAKE_GENERATOR_PLATFORM") == "Tegra-Android") {
444       if (!this->InitializeTegraAndroid(mf)) {
445         return false;
446       }
447     } else {
448       this->SystemIsAndroid = true;
449       if (!this->InitializeAndroid(mf)) {
450         return false;
451       }
452     }
453   }
454
455   return true;
456 }
457
458 bool cmGlobalVisualStudio10Generator::InitializeWindows(cmMakefile*)
459 {
460   return true;
461 }
462
463 bool cmGlobalVisualStudio10Generator::InitializeWindowsCE(cmMakefile* mf)
464 {
465   if (this->PlatformInGeneratorName) {
466     std::ostringstream e;
467     e << "CMAKE_SYSTEM_NAME is 'WindowsCE' but CMAKE_GENERATOR "
468       << "specifies a platform too: '" << this->GetName() << "'";
469     mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
470     return false;
471   }
472
473   this->DefaultPlatformToolset = this->SelectWindowsCEToolset();
474
475   if (this->GetVersion() == cmGlobalVisualStudioGenerator::VSVersion::VS12) {
476     // VS 12 .NET CF defaults to .NET framework 3.9 for Windows CE.
477     this->DefaultTargetFrameworkVersion = "v3.9";
478     this->DefaultTargetFrameworkIdentifier = "WindowsEmbeddedCompact";
479     this->DefaultTargetFrameworkTargetsVersion = "v8.0";
480   }
481
482   return true;
483 }
484
485 bool cmGlobalVisualStudio10Generator::InitializeWindowsPhone(cmMakefile* mf)
486 {
487   std::ostringstream e;
488   e << this->GetName() << " does not support Windows Phone.";
489   mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
490   return false;
491 }
492
493 bool cmGlobalVisualStudio10Generator::InitializeWindowsStore(cmMakefile* mf)
494 {
495   std::ostringstream e;
496   e << this->GetName() << " does not support Windows Store.";
497   mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
498   return false;
499 }
500
501 bool cmGlobalVisualStudio10Generator::InitializeTegraAndroid(cmMakefile* mf)
502 {
503   std::string v = this->GetInstalledNsightTegraVersion();
504   if (v.empty()) {
505     mf->IssueMessage(MessageType::FATAL_ERROR,
506                      "CMAKE_SYSTEM_NAME is 'Android' but "
507                      "'NVIDIA Nsight Tegra Visual Studio Edition' "
508                      "is not installed.");
509     return false;
510   }
511   this->DefaultPlatformName = "Tegra-Android";
512   this->DefaultPlatformToolset = "Default";
513   this->NsightTegraVersion = v;
514   mf->AddDefinition("CMAKE_VS_NsightTegra_VERSION", v);
515   return true;
516 }
517
518 bool cmGlobalVisualStudio10Generator::InitializeAndroid(cmMakefile* mf)
519 {
520   std::ostringstream e;
521   e << this->GetName() << " does not support Android.";
522   mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
523   return false;
524 }
525
526 bool cmGlobalVisualStudio10Generator::SelectWindowsPhoneToolset(
527   std::string& toolset) const
528 {
529   toolset.clear();
530   return false;
531 }
532
533 bool cmGlobalVisualStudio10Generator::SelectWindowsStoreToolset(
534   std::string& toolset) const
535 {
536   toolset.clear();
537   return false;
538 }
539
540 std::string cmGlobalVisualStudio10Generator::SelectWindowsCEToolset() const
541 {
542   if (this->SystemVersion == "8.0") {
543     return "CE800";
544   }
545   return "";
546 }
547
548 //! Create a local generator appropriate to this Global Generator
549 std::unique_ptr<cmLocalGenerator>
550 cmGlobalVisualStudio10Generator::CreateLocalGenerator(cmMakefile* mf)
551 {
552   return std::unique_ptr<cmLocalGenerator>(
553     cm::make_unique<cmLocalVisualStudio10Generator>(this, mf));
554 }
555
556 void cmGlobalVisualStudio10Generator::Generate()
557 {
558   this->LongestSource = LongestSourcePath();
559   this->cmGlobalVisualStudio8Generator::Generate();
560   if (!this->AndroidExecutableWarnings.empty() &&
561       !this->CMakeInstance->GetIsInTryCompile()) {
562     std::ostringstream e;
563     /* clang-format off */
564     e <<
565       "You are using Visual Studio tools for Android, which does not support "
566       "standalone executables. However, the following executable targets do "
567       "not have the ANDROID_GUI property set, and thus will not be built as "
568       "expected. They will be built as shared libraries with executable "
569       "filenames:\n"
570       "  ";
571     /* clang-format on */
572     bool first = true;
573     for (auto const& name : this->AndroidExecutableWarnings) {
574       if (!first) {
575         e << ", ";
576       }
577       first = false;
578       e << name;
579     }
580     this->CMakeInstance->IssueMessage(MessageType::WARNING, e.str());
581   }
582   if (this->LongestSource.Length > 0) {
583     cmLocalGenerator* lg = this->LongestSource.Target->GetLocalGenerator();
584     std::ostringstream e;
585     /* clang-format off */
586     e <<
587       "The binary and/or source directory paths may be too long to generate "
588       "Visual Studio 10 files for this project.  "
589       "Consider choosing shorter directory names to build this project with "
590       "Visual Studio 10.  "
591       "A more detailed explanation follows."
592       "\n"
593       "There is a bug in the VS 10 IDE that renders property dialog fields "
594       "blank for files referenced by full path in the project file.  "
595       "However, CMake must reference at least one file by full path:\n"
596       "  " << this->LongestSource.SourceFile->GetFullPath() << "\n"
597       "This is because some Visual Studio tools would append the relative "
598       "path to the end of the referencing directory path, as in:\n"
599       "  " << lg->GetCurrentBinaryDirectory() << "/"
600       << this->LongestSource.SourceRel << "\n"
601       "and then incorrectly complain that the file does not exist because "
602       "the path length is too long for some internal buffer or API.  "
603       "To avoid this problem CMake must use a full path for this file "
604       "which then triggers the VS 10 property dialog bug.";
605     /* clang-format on */
606     lg->IssueMessage(MessageType::WARNING, e.str());
607   }
608   if (cmValue cached = this->CMakeInstance->GetState()->GetCacheEntryValue(
609         "CMAKE_VS_NUGET_PACKAGE_RESTORE")) {
610     this->CMakeInstance->MarkCliAsUsed("CMAKE_VS_NUGET_PACKAGE_RESTORE");
611   }
612 }
613
614 void cmGlobalVisualStudio10Generator::EnableLanguage(
615   std::vector<std::string> const& lang, cmMakefile* mf, bool optional)
616 {
617   for (std::string const& it : lang) {
618     if (it == "ASM_NASM") {
619       this->NasmEnabled = true;
620     }
621     if (it == "CUDA") {
622       this->CudaEnabled = true;
623     }
624   }
625   this->AddPlatformDefinitions(mf);
626   cmGlobalVisualStudio8Generator::EnableLanguage(lang, mf, optional);
627 }
628
629 const char* cmGlobalVisualStudio10Generator::GetCustomVCTargetsPath() const
630 {
631   if (this->CustomVCTargetsPath.empty()) {
632     return nullptr;
633   }
634   return this->CustomVCTargetsPath.c_str();
635 }
636
637 const char* cmGlobalVisualStudio10Generator::GetPlatformToolset() const
638 {
639   std::string const& toolset = this->GetPlatformToolsetString();
640   if (toolset.empty()) {
641     return nullptr;
642   }
643   return toolset.c_str();
644 }
645
646 std::string const& cmGlobalVisualStudio10Generator::GetPlatformToolsetString()
647   const
648 {
649   if (!this->GeneratorToolset.empty()) {
650     return this->GeneratorToolset;
651   }
652   if (this->SystemIsAndroid) {
653     if (!this->DefaultAndroidToolset.empty()) {
654       return this->DefaultAndroidToolset;
655     }
656   } else {
657     if (!this->DefaultPlatformToolset.empty()) {
658       return this->DefaultPlatformToolset;
659     }
660   }
661   static std::string const empty;
662   return empty;
663 }
664
665 std::string const&
666 cmGlobalVisualStudio10Generator::GetPlatformToolsetVersionProps() const
667 {
668   return this->GeneratorToolsetVersionProps;
669 }
670
671 const char*
672 cmGlobalVisualStudio10Generator::GetPlatformToolsetHostArchitecture() const
673 {
674   std::string const& hostArch =
675     this->GetPlatformToolsetHostArchitectureString();
676   if (hostArch.empty()) {
677     return nullptr;
678   }
679   return hostArch.c_str();
680 }
681
682 std::string const&
683 cmGlobalVisualStudio10Generator::GetPlatformToolsetHostArchitectureString()
684   const
685 {
686   if (!this->GeneratorToolsetHostArchitecture.empty()) {
687     return this->GeneratorToolsetHostArchitecture;
688   }
689   if (!this->DefaultPlatformToolsetHostArchitecture.empty()) {
690     return this->DefaultPlatformToolsetHostArchitecture;
691   }
692   static std::string const empty;
693   return empty;
694 }
695
696 const char* cmGlobalVisualStudio10Generator::GetPlatformToolsetCuda() const
697 {
698   if (!this->GeneratorToolsetCuda.empty()) {
699     return this->GeneratorToolsetCuda.c_str();
700   }
701   return nullptr;
702 }
703
704 std::string const&
705 cmGlobalVisualStudio10Generator::GetPlatformToolsetCudaString() const
706 {
707   return this->GeneratorToolsetCuda;
708 }
709
710 const char* cmGlobalVisualStudio10Generator::GetPlatformToolsetCudaCustomDir()
711   const
712 {
713   if (!this->GeneratorToolsetCudaCustomDir.empty()) {
714     return this->GeneratorToolsetCudaCustomDir.c_str();
715   }
716   return nullptr;
717 }
718
719 std::string const&
720 cmGlobalVisualStudio10Generator::GetPlatformToolsetCudaCustomDirString() const
721 {
722   return this->GeneratorToolsetCudaCustomDir;
723 }
724
725 std::string const&
726 cmGlobalVisualStudio10Generator::GetPlatformToolsetCudaNvccSubdirString() const
727 {
728   return this->GeneratorToolsetCudaNvccSubdir;
729 }
730
731 std::string const& cmGlobalVisualStudio10Generator::
732   GetPlatformToolsetCudaVSIntegrationSubdirString() const
733 {
734   return this->GeneratorToolsetCudaVSIntegrationSubdir;
735 }
736
737 cmGlobalVisualStudio10Generator::AuxToolset
738 cmGlobalVisualStudio10Generator::FindAuxToolset(std::string&,
739                                                 std::string&) const
740 {
741   return AuxToolset::None;
742 }
743
744 bool cmGlobalVisualStudio10Generator::FindMakeProgram(cmMakefile* mf)
745 {
746   if (!this->cmGlobalVisualStudio8Generator::FindMakeProgram(mf)) {
747     return false;
748   }
749   mf->AddDefinition("CMAKE_VS_MSBUILD_COMMAND", this->GetMSBuildCommand());
750   return true;
751 }
752
753 std::string const& cmGlobalVisualStudio10Generator::GetMSBuildCommand()
754 {
755   if (!this->MSBuildCommandInitialized) {
756     this->MSBuildCommandInitialized = true;
757     this->MSBuildCommand = this->FindMSBuildCommand();
758   }
759   return this->MSBuildCommand;
760 }
761
762 cm::optional<std::string>
763 cmGlobalVisualStudio10Generator::FindMSBuildCommandEarly(cmMakefile*)
764 {
765   return this->GetMSBuildCommand();
766 }
767
768 std::string cmGlobalVisualStudio10Generator::FindMSBuildCommand()
769 {
770   std::string msbuild;
771   std::string mskey;
772
773   // Search in standard location.
774   mskey = cmStrCat(
775     "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\MSBuild\\ToolsVersions\\",
776     this->GetToolsVersion(), ";MSBuildToolsPath");
777   if (cmSystemTools::ReadRegistryValue(mskey.c_str(), msbuild,
778                                        cmSystemTools::KeyWOW64_32)) {
779     cmSystemTools::ConvertToUnixSlashes(msbuild);
780     msbuild += "/MSBuild.exe";
781     if (cmSystemTools::FileExists(msbuild, true)) {
782       return msbuild;
783     }
784   }
785
786   msbuild = "MSBuild.exe";
787   return msbuild;
788 }
789
790 std::string cmGlobalVisualStudio10Generator::FindDevEnvCommand()
791 {
792   if (this->ExpressEdition) {
793     // Visual Studio Express >= 10 do not have "devenv.com" or
794     // "VCExpress.exe" that we can use to build reliably.
795     // Tell the caller it needs to use MSBuild instead.
796     return "";
797   }
798   // Skip over the cmGlobalVisualStudio8Generator implementation because
799   // we expect a real devenv and do not want to look for VCExpress.
800   return this->cmGlobalVisualStudio71Generator::FindDevEnvCommand();
801 }
802
803 bool cmGlobalVisualStudio10Generator::FindVCTargetsPath(cmMakefile* mf)
804 {
805   // Skip this in special cases within our own test suite.
806   if (this->GetPlatformName() == "Test Platform" ||
807       this->GetPlatformToolsetString() == "Test Toolset") {
808     return true;
809   }
810
811   std::string wd;
812   if (!this->ConfiguredFilesPath.empty()) {
813     // In a try-compile we are given the outer CMakeFiles directory.
814     wd = this->ConfiguredFilesPath;
815   } else {
816     wd = cmStrCat(this->GetCMakeInstance()->GetHomeOutputDirectory(),
817                   "/CMakeFiles");
818   }
819   wd += "/";
820   wd += cmVersion::GetCMakeVersion();
821
822   // We record the result persistently in a file.
823   std::string const txt = wd + "/VCTargetsPath.txt";
824
825   // If we have a recorded result, use it.
826   {
827     cmsys::ifstream fin(txt.c_str());
828     if (fin && cmSystemTools::GetLineFromStream(fin, this->VCTargetsPath) &&
829         cmSystemTools::FileIsDirectory(this->VCTargetsPath)) {
830       cmSystemTools::ConvertToUnixSlashes(this->VCTargetsPath);
831       return true;
832     }
833   }
834
835   // Prepare the work directory.
836   if (!cmSystemTools::MakeDirectory(wd)) {
837     std::string e = "Failed to make directory:\n  " + wd;
838     mf->IssueMessage(MessageType::FATAL_ERROR, e);
839     cmSystemTools::SetFatalErrorOccurred();
840     return false;
841   }
842
843   // Generate a project file for MSBuild to tell us the VCTargetsPath value.
844   std::string const vcxproj = "VCTargetsPath.vcxproj";
845   {
846     std::string const vcxprojAbs = wd + "/" + vcxproj;
847     cmsys::ofstream fout(vcxprojAbs.c_str());
848     cmXMLWriter xw(fout);
849
850     cmXMLDocument doc(xw);
851     cmXMLElement eprj(doc, "Project");
852     eprj.Attribute("DefaultTargets", "Build");
853     eprj.Attribute("ToolsVersion", "4.0");
854     eprj.Attribute("xmlns",
855                    "http://schemas.microsoft.com/developer/msbuild/2003");
856     if (this->IsNsightTegra()) {
857       cmXMLElement epg(eprj, "PropertyGroup");
858       epg.Attribute("Label", "NsightTegraProject");
859       cmXMLElement(epg, "NsightTegraProjectRevisionNumber").Content("6");
860     }
861     {
862       cmXMLElement eig(eprj, "ItemGroup");
863       eig.Attribute("Label", "ProjectConfigurations");
864       cmXMLElement epc(eig, "ProjectConfiguration");
865       epc.Attribute("Include", "Debug|" + this->GetPlatformName());
866       cmXMLElement(epc, "Configuration").Content("Debug");
867       cmXMLElement(epc, "Platform").Content(this->GetPlatformName());
868     }
869     {
870       cmXMLElement epg(eprj, "PropertyGroup");
871       epg.Attribute("Label", "Globals");
872       cmXMLElement(epg, "ProjectGuid")
873         .Content("{F3FC6D86-508D-3FB1-96D2-995F08B142EC}");
874       cmXMLElement(epg, "Keyword")
875         .Content(mf->GetSafeDefinition("CMAKE_SYSTEM_NAME") == "Android"
876                    ? "Android"
877                    : "Win32Proj");
878       cmXMLElement(epg, "Platform").Content(this->GetPlatformName());
879       if (this->GetSystemName() == "WindowsPhone") {
880         cmXMLElement(epg, "ApplicationType").Content("Windows Phone");
881         cmXMLElement(epg, "ApplicationTypeRevision")
882           .Content(this->GetApplicationTypeRevision());
883       } else if (this->GetSystemName() == "WindowsStore") {
884         cmXMLElement(epg, "ApplicationType").Content("Windows Store");
885         cmXMLElement(epg, "ApplicationTypeRevision")
886           .Content(this->GetApplicationTypeRevision());
887       } else if (this->GetSystemName() == "Android") {
888         cmXMLElement(epg, "ApplicationType").Content("Android");
889         cmXMLElement(epg, "ApplicationTypeRevision")
890           .Content(this->GetApplicationTypeRevision());
891       }
892       if (!this->WindowsTargetPlatformVersion.empty()) {
893         cmXMLElement(epg, "WindowsTargetPlatformVersion")
894           .Content(this->WindowsTargetPlatformVersion);
895       }
896       if (this->GetSystemName() != "Android") {
897         if (this->GetPlatformName() == "ARM64") {
898           cmXMLElement(epg, "WindowsSDKDesktopARM64Support").Content("true");
899         } else if (this->GetPlatformName() == "ARM") {
900           cmXMLElement(epg, "WindowsSDKDesktopARMSupport").Content("true");
901         }
902       }
903     }
904     cmXMLElement(eprj, "Import")
905       .Attribute("Project", "$(VCTargetsPath)\\Microsoft.Cpp.Default.props");
906     if (const char* hostArch = this->GetPlatformToolsetHostArchitecture()) {
907       cmXMLElement epg(eprj, "PropertyGroup");
908       cmXMLElement(epg, "PreferredToolArchitecture").Content(hostArch);
909     }
910     {
911       cmXMLElement epg(eprj, "PropertyGroup");
912       epg.Attribute("Label", "Configuration");
913       {
914         cmXMLElement ect(epg, "ConfigurationType");
915         if (this->IsNsightTegra()) {
916           // Tegra-Android platform does not understand "Utility".
917           ect.Content("StaticLibrary");
918         } else {
919           ect.Content("Utility");
920         }
921       }
922       cmXMLElement(epg, "CharacterSet").Content("MultiByte");
923       if (this->IsNsightTegra()) {
924         cmXMLElement(epg, "NdkToolchainVersion")
925           .Content(this->GetPlatformToolsetString());
926       } else {
927         cmXMLElement(epg, "PlatformToolset")
928           .Content(this->GetPlatformToolsetString());
929       }
930     }
931     cmXMLElement(eprj, "Import")
932       .Attribute("Project", "$(VCTargetsPath)\\Microsoft.Cpp.props");
933     {
934       cmXMLElement eidg(eprj, "ItemDefinitionGroup");
935       cmXMLElement epbe(eidg, "PostBuildEvent");
936       cmXMLElement(epbe, "Command")
937         .Content("echo VCTargetsPath=$(VCTargetsPath)");
938     }
939     cmXMLElement(eprj, "Import")
940       .Attribute("Project", "$(VCTargetsPath)\\Microsoft.Cpp.targets");
941   }
942
943   std::vector<std::string> cmd;
944   cmd.push_back(this->GetMSBuildCommand());
945   cmd.push_back(vcxproj);
946   cmd.push_back("/p:Configuration=Debug");
947   cmd.push_back(cmStrCat("/p:Platform=", this->GetPlatformName()));
948   cmd.push_back(std::string("/p:VisualStudioVersion=") +
949                 this->GetIDEVersion());
950   std::string out;
951   int ret = 0;
952   cmsys::RegularExpression regex("\n *VCTargetsPath=([^%\r\n]+)[\r\n]");
953   if (!cmSystemTools::RunSingleCommand(cmd, &out, &out, &ret, wd.c_str(),
954                                        cmSystemTools::OUTPUT_NONE) ||
955       ret != 0 || !regex.find(out)) {
956     cmSystemTools::ReplaceString(out, "\n", "\n  ");
957     std::ostringstream e;
958     /* clang-format off */
959     e <<
960       "Failed to run MSBuild command:\n"
961       "  " << cmd[0] << "\n"
962       "to get the value of VCTargetsPath:\n"
963       "  " << out << "\n"
964       ;
965     /* clang-format on */
966     if (ret != 0) {
967       e << "Exit code: " << ret << "\n";
968     }
969     mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
970     cmSystemTools::SetFatalErrorOccurred();
971     return false;
972   }
973   this->VCTargetsPath = regex.match(1);
974   cmSystemTools::ConvertToUnixSlashes(this->VCTargetsPath);
975
976   {
977     cmsys::ofstream fout(txt.c_str());
978     fout << this->VCTargetsPath << "\n";
979   }
980   return true;
981 }
982
983 std::vector<cmGlobalGenerator::GeneratedMakeCommand>
984 cmGlobalVisualStudio10Generator::GenerateBuildCommand(
985   const std::string& makeProgram, const std::string& projectName,
986   const std::string& projectDir, std::vector<std::string> const& targetNames,
987   const std::string& config, int jobs, bool verbose,
988   const cmBuildOptions& buildOptions,
989   std::vector<std::string> const& makeOptions)
990 {
991   std::vector<GeneratedMakeCommand> makeCommands;
992   // Select the caller- or user-preferred make program, else MSBuild.
993   std::string makeProgramSelected =
994     this->SelectMakeProgram(makeProgram, this->GetMSBuildCommand());
995
996   // Check if the caller explicitly requested a devenv tool.
997   std::string makeProgramLower = makeProgramSelected;
998   cmSystemTools::LowerCase(makeProgramLower);
999   bool useDevEnv = (makeProgramLower.find("devenv") != std::string::npos ||
1000                     makeProgramLower.find("vcexpress") != std::string::npos);
1001
1002   // Workaround to convince VCExpress.exe to produce output.
1003   const bool requiresOutputForward =
1004     (makeProgramLower.find("vcexpress") != std::string::npos);
1005
1006   // MSBuild is preferred (and required for VS Express), but if the .sln has
1007   // an Intel Fortran .vfproj then we have to use devenv. Parse it to find out.
1008   cmSlnData slnData;
1009   {
1010     std::string slnFile;
1011     if (!projectDir.empty()) {
1012       slnFile = cmStrCat(projectDir, '/');
1013     }
1014     slnFile += projectName;
1015     slnFile += ".sln";
1016     cmVisualStudioSlnParser parser;
1017     if (parser.ParseFile(slnFile, slnData,
1018                          cmVisualStudioSlnParser::DataGroupAll)) {
1019       std::vector<cmSlnProjectEntry> slnProjects = slnData.GetProjects();
1020       for (cmSlnProjectEntry const& project : slnProjects) {
1021         if (useDevEnv) {
1022           break;
1023         }
1024         std::string proj = project.GetRelativePath();
1025         if (proj.size() > 7 && proj.substr(proj.size() - 7) == ".vfproj") {
1026           useDevEnv = true;
1027         }
1028       }
1029     }
1030   }
1031   if (useDevEnv) {
1032     // Use devenv to build solutions containing Intel Fortran projects.
1033     return cmGlobalVisualStudio7Generator::GenerateBuildCommand(
1034       makeProgram, projectName, projectDir, targetNames, config, jobs, verbose,
1035       buildOptions, makeOptions);
1036   }
1037
1038   std::vector<std::string> realTargetNames = targetNames;
1039   if (targetNames.empty() ||
1040       ((targetNames.size() == 1) && targetNames.front().empty())) {
1041     realTargetNames = { "ALL_BUILD" };
1042   }
1043   for (const auto& tname : realTargetNames) {
1044     // msbuild.exe CxxOnly.sln /t:Build /p:Configuration=Debug
1045     // /target:ALL_BUILD
1046     //                         /m
1047     if (tname.empty()) {
1048       continue;
1049     }
1050
1051     GeneratedMakeCommand makeCommand;
1052     makeCommand.RequiresOutputForward = requiresOutputForward;
1053     makeCommand.Add(makeProgramSelected);
1054     cm::optional<cmSlnProjectEntry> proj = cm::nullopt;
1055
1056     if (tname == "clean") {
1057       makeCommand.Add(cmStrCat(projectName, ".sln"));
1058       makeCommand.Add("/t:Clean");
1059     } else {
1060       std::string targetProject = cmStrCat(tname, ".vcxproj");
1061       proj = slnData.GetProjectByName(tname);
1062       if (targetProject.find('/') == std::string::npos) {
1063         // it might be in a subdir
1064         if (proj) {
1065           targetProject = proj->GetRelativePath();
1066           cmSystemTools::ConvertToUnixSlashes(targetProject);
1067         }
1068       }
1069       makeCommand.Add(targetProject);
1070
1071       // Check if we do need a restore at all (i.e. if there are package
1072       // references and restore has not been disabled by a command line option.
1073       PackageResolveMode restoreMode = buildOptions.ResolveMode;
1074       bool requiresRestore = true;
1075
1076       if (restoreMode == PackageResolveMode::Disable) {
1077         requiresRestore = false;
1078       } else if (cmValue cached =
1079                    this->CMakeInstance->GetState()->GetCacheEntryValue(
1080                      tname + "_REQUIRES_VS_PACKAGE_RESTORE")) {
1081         requiresRestore = cached.IsOn();
1082       } else {
1083         // There are no package references defined.
1084         requiresRestore = false;
1085       }
1086
1087       // If a restore is required, evaluate the restore mode.
1088       if (requiresRestore) {
1089         if (restoreMode == PackageResolveMode::OnlyResolve) {
1090           // Only invoke the restore target on the project.
1091           makeCommand.Add("/t:Restore");
1092         } else {
1093           // Invoke restore target, unless it has been explicitly disabled.
1094           bool restorePackages = true;
1095
1096           if (this->Version < VSVersion::VS15) {
1097             // Package restore is only supported starting from Visual Studio
1098             // 2017. Package restore must be executed manually using NuGet
1099             // shell for older versions.
1100             this->CMakeInstance->IssueMessage(
1101               MessageType::WARNING,
1102               "Restoring package references is only supported for Visual "
1103               "Studio 2017 and later. You have to manually restore the "
1104               "packages using NuGet before building the project.");
1105             restorePackages = false;
1106           } else if (restoreMode == PackageResolveMode::Default) {
1107             // Decide if a restore is performed, based on a cache variable.
1108             if (cmValue cached =
1109                   this->CMakeInstance->GetState()->GetCacheEntryValue(
1110                     "CMAKE_VS_NUGET_PACKAGE_RESTORE"))
1111               restorePackages = cached.IsOn();
1112           }
1113
1114           if (restorePackages) {
1115             if (this->IsMsBuildRestoreSupported()) {
1116               makeCommand.Add("/restore");
1117             } else {
1118               GeneratedMakeCommand restoreCommand;
1119               restoreCommand.Add(makeProgramSelected);
1120               restoreCommand.Add(targetProject);
1121               restoreCommand.Add("/t:Restore");
1122               makeCommands.emplace_back(restoreCommand);
1123             }
1124           }
1125         }
1126       }
1127     }
1128
1129     std::string plainConfig = config;
1130     if (config.empty()) {
1131       plainConfig = "Debug";
1132     }
1133
1134     std::string platform = GetPlatformName();
1135     if (proj) {
1136       std::string extension =
1137         cmSystemTools::GetFilenameLastExtension(proj->GetRelativePath());
1138       extension = cmSystemTools::LowerCase(extension);
1139       if (extension.compare(".csproj") == 0) {
1140         // Use correct platform name
1141         platform =
1142           slnData.GetConfigurationTarget(tname, plainConfig, platform);
1143       }
1144     }
1145
1146     makeCommand.Add(cmStrCat("/p:Configuration=", plainConfig));
1147     makeCommand.Add(cmStrCat("/p:Platform=", platform));
1148     makeCommand.Add(
1149       cmStrCat("/p:VisualStudioVersion=", this->GetIDEVersion()));
1150
1151     if (jobs != cmake::NO_BUILD_PARALLEL_LEVEL) {
1152       if (jobs == cmake::DEFAULT_BUILD_PARALLEL_LEVEL) {
1153         makeCommand.Add("/m");
1154       } else {
1155         makeCommand.Add(cmStrCat("/m:", std::to_string(jobs)));
1156       }
1157       // Having msbuild.exe and cl.exe using multiple jobs is discouraged
1158       makeCommand.Add("/p:CL_MPCount=1");
1159     }
1160
1161     // Respect the verbosity: 'n' normal will show build commands
1162     //                        'm' minimal only the build step's title
1163     makeCommand.Add(cmStrCat("/v:", ((verbose) ? "n" : "m")));
1164     makeCommand.Add(makeOptions.begin(), makeOptions.end());
1165     makeCommands.emplace_back(std::move(makeCommand));
1166   }
1167   return makeCommands;
1168 }
1169
1170 std::string cmGlobalVisualStudio10Generator::GenerateRuleFile(
1171   std::string const& output) const
1172 {
1173   // The VS 10 generator needs to create the .rule files on disk.
1174   // Hide them away under the CMakeFiles directory.
1175   std::string ruleDir = cmStrCat(
1176     this->GetCMakeInstance()->GetHomeOutputDirectory(), "/CMakeFiles/",
1177     cmSystemTools::ComputeStringMD5(cmSystemTools::GetFilenamePath(output)));
1178   std::string ruleFile =
1179     cmStrCat(ruleDir, '/', cmSystemTools::GetFilenameName(output), ".rule");
1180   return ruleFile;
1181 }
1182
1183 void cmGlobalVisualStudio10Generator::PathTooLong(cmGeneratorTarget* target,
1184                                                   cmSourceFile const* sf,
1185                                                   std::string const& sfRel)
1186 {
1187   size_t len =
1188     (target->GetLocalGenerator()->GetCurrentBinaryDirectory().length() + 1 +
1189      sfRel.length());
1190   if (len > this->LongestSource.Length) {
1191     this->LongestSource.Length = len;
1192     this->LongestSource.Target = target;
1193     this->LongestSource.SourceFile = sf;
1194     this->LongestSource.SourceRel = sfRel;
1195   }
1196 }
1197
1198 std::string cmGlobalVisualStudio10Generator::Encoding()
1199 {
1200   return "utf-8";
1201 }
1202
1203 const char* cmGlobalVisualStudio10Generator::GetToolsVersion() const
1204 {
1205   switch (this->Version) {
1206     case cmGlobalVisualStudioGenerator::VSVersion::VS9:
1207     case cmGlobalVisualStudioGenerator::VSVersion::VS11:
1208       return "4.0";
1209
1210       // in Visual Studio 2013 they detached the MSBuild tools version
1211       // from the .Net Framework version and instead made it have it's own
1212       // version number
1213     case cmGlobalVisualStudioGenerator::VSVersion::VS12:
1214       return "12.0";
1215     case cmGlobalVisualStudioGenerator::VSVersion::VS14:
1216       return "14.0";
1217     case cmGlobalVisualStudioGenerator::VSVersion::VS15:
1218       return "15.0";
1219     case cmGlobalVisualStudioGenerator::VSVersion::VS16:
1220       return "16.0";
1221     case cmGlobalVisualStudioGenerator::VSVersion::VS17:
1222       return "17.0";
1223   }
1224   return "";
1225 }
1226
1227 bool cmGlobalVisualStudio10Generator::IsNsightTegra() const
1228 {
1229   return !this->NsightTegraVersion.empty();
1230 }
1231
1232 std::string cmGlobalVisualStudio10Generator::GetNsightTegraVersion() const
1233 {
1234   return this->NsightTegraVersion;
1235 }
1236
1237 std::string cmGlobalVisualStudio10Generator::GetInstalledNsightTegraVersion()
1238 {
1239   std::string version;
1240   cmSystemTools::ReadRegistryValue(
1241     "HKEY_LOCAL_MACHINE\\SOFTWARE\\NVIDIA Corporation\\Nsight Tegra;"
1242     "Version",
1243     version, cmSystemTools::KeyWOW64_32);
1244   return version;
1245 }
1246
1247 std::string cmGlobalVisualStudio10Generator::GetApplicationTypeRevision() const
1248 {
1249   if (this->GetSystemName() == "Android") {
1250     return this->GetAndroidApplicationTypeRevision();
1251   }
1252
1253   // Return the first two '.'-separated components of the Windows version.
1254   std::string::size_type end1 = this->SystemVersion.find('.');
1255   std::string::size_type end2 =
1256     end1 == std::string::npos ? end1 : this->SystemVersion.find('.', end1 + 1);
1257   return this->SystemVersion.substr(0, end2);
1258 }
1259
1260 static std::string cmLoadFlagTableString(Json::Value entry, const char* field)
1261 {
1262   if (entry.isMember(field)) {
1263     auto string = entry[field];
1264     if (string.isConvertibleTo(Json::ValueType::stringValue)) {
1265       return string.asString();
1266     }
1267   }
1268   return "";
1269 }
1270
1271 static unsigned int cmLoadFlagTableSpecial(Json::Value entry,
1272                                            const char* field)
1273 {
1274   unsigned int value = 0;
1275   if (entry.isMember(field)) {
1276     auto specials = entry[field];
1277     if (specials.isArray()) {
1278       for (auto const& special : specials) {
1279         std::string s = special.asString();
1280         if (s == "UserValue") {
1281           value |= cmIDEFlagTable::UserValue;
1282         } else if (s == "UserIgnored") {
1283           value |= cmIDEFlagTable::UserIgnored;
1284         } else if (s == "UserRequired") {
1285           value |= cmIDEFlagTable::UserRequired;
1286         } else if (s == "Continue") {
1287           value |= cmIDEFlagTable::Continue;
1288         } else if (s == "SemicolonAppendable") {
1289           value |= cmIDEFlagTable::SemicolonAppendable;
1290         } else if (s == "UserFollowing") {
1291           value |= cmIDEFlagTable::UserFollowing;
1292         } else if (s == "CaseInsensitive") {
1293           value |= cmIDEFlagTable::CaseInsensitive;
1294         } else if (s == "SpaceAppendable") {
1295           value |= cmIDEFlagTable::SpaceAppendable;
1296         } else if (s == "CommaAppendable") {
1297           value |= cmIDEFlagTable::CommaAppendable;
1298         }
1299       }
1300     }
1301   }
1302   return value;
1303 }
1304
1305 namespace {
1306
1307 cmIDEFlagTable const* cmLoadFlagTableJson(std::string const& flagJsonPath,
1308                                           cm::optional<std::string> vsVer)
1309 {
1310   cmIDEFlagTable* ret = nullptr;
1311   auto savedFlagIterator = loadedFlagJsonFiles.find(flagJsonPath);
1312   if (savedFlagIterator != loadedFlagJsonFiles.end()) {
1313     ret = savedFlagIterator->second.data();
1314   } else {
1315     Json::Reader reader;
1316     cmsys::ifstream stream;
1317
1318     stream.open(flagJsonPath.c_str(), std::ios_base::in);
1319     if (stream) {
1320       Json::Value flags;
1321       if (reader.parse(stream, flags, false) && flags.isArray()) {
1322         std::vector<cmIDEFlagTable> flagTable;
1323         for (auto const& flag : flags) {
1324           Json::Value const& vsminJson = flag["vsmin"];
1325           if (vsminJson.isString()) {
1326             std::string const& vsmin = vsminJson.asString();
1327             if (!vsmin.empty()) {
1328               if (!vsVer ||
1329                   cmSystemTools::VersionCompareGreater(vsmin, *vsVer)) {
1330                 continue;
1331               }
1332             }
1333           }
1334           cmIDEFlagTable flagEntry;
1335           flagEntry.IDEName = cmLoadFlagTableString(flag, "name");
1336           flagEntry.commandFlag = cmLoadFlagTableString(flag, "switch");
1337           flagEntry.comment = cmLoadFlagTableString(flag, "comment");
1338           flagEntry.value = cmLoadFlagTableString(flag, "value");
1339           flagEntry.special = cmLoadFlagTableSpecial(flag, "flags");
1340           flagTable.push_back(flagEntry);
1341         }
1342         cmIDEFlagTable endFlag{ "", "", "", "", 0 };
1343         flagTable.push_back(endFlag);
1344
1345         loadedFlagJsonFiles[flagJsonPath] = flagTable;
1346         ret = loadedFlagJsonFiles[flagJsonPath].data();
1347       }
1348     }
1349   }
1350   return ret;
1351 }
1352 }
1353
1354 cm::optional<std::string> cmGlobalVisualStudio10Generator::FindFlagTable(
1355   cm::string_view toolsetName, cm::string_view table) const
1356 {
1357   if (!this->CustomFlagTableDir.empty()) {
1358     std::string customFlagTableFile =
1359       cmStrCat(this->CustomFlagTableDir, '/', this->GetPlatformName(), '_',
1360                toolsetName, '_', table, ".json");
1361     if (cmSystemTools::FileExists(customFlagTableFile)) {
1362       return customFlagTableFile;
1363     }
1364     customFlagTableFile =
1365       cmStrCat(this->CustomFlagTableDir, '/', this->GetPlatformName(), '_',
1366                table, ".json");
1367     if (cmSystemTools::FileExists(customFlagTableFile)) {
1368       return customFlagTableFile;
1369     }
1370   }
1371   std::string fullPath =
1372     cmStrCat(cmSystemTools::GetCMakeRoot(), "/Templates/MSBuild/FlagTables/",
1373              toolsetName, '_', table, ".json");
1374   if (cmSystemTools::FileExists(fullPath)) {
1375     return fullPath;
1376   }
1377   return {};
1378 }
1379
1380 cmIDEFlagTable const* cmGlobalVisualStudio10Generator::LoadFlagTable(
1381   std::string const& toolSpecificName, std::string const& defaultName,
1382   std::string const& table) const
1383 {
1384   cmMakefile* mf = this->GetCurrentMakefile();
1385
1386   std::string filename;
1387   if (!toolSpecificName.empty()) {
1388     if (cm::optional<std::string> found =
1389           this->FindFlagTable(toolSpecificName, table)) {
1390       filename = std::move(*found);
1391     } else {
1392       mf->IssueMessage(MessageType::FATAL_ERROR,
1393                        cmStrCat("JSON flag table for ", table,
1394                                 " not found for toolset ", toolSpecificName));
1395       return nullptr;
1396     }
1397   } else {
1398     std::string const& genericName =
1399       this->CanonicalToolsetName(this->GetPlatformToolsetString());
1400     cm::optional<std::string> found = this->FindFlagTable(genericName, table);
1401     if (!found) {
1402       found = this->FindFlagTable(defaultName, table);
1403     }
1404     if (found) {
1405       filename = std::move(*found);
1406     } else {
1407       mf->IssueMessage(MessageType::FATAL_ERROR,
1408                        cmStrCat("JSON flag table for ", table,
1409                                 " not found for toolset ", genericName, " ",
1410                                 defaultName));
1411       return nullptr;
1412     }
1413   }
1414
1415   cm::optional<std::string> vsVer = this->GetVSInstanceVersion();
1416   if (cmIDEFlagTable const* ret = cmLoadFlagTableJson(filename, vsVer)) {
1417     return ret;
1418   }
1419
1420   mf->IssueMessage(
1421     MessageType::FATAL_ERROR,
1422     cmStrCat("JSON flag table could not be loaded:\n  ", filename));
1423   return nullptr;
1424 }
1425
1426 cmIDEFlagTable const* cmGlobalVisualStudio10Generator::GetClFlagTable() const
1427 {
1428   return LoadFlagTable(this->GetClFlagTableName(),
1429                        this->DefaultCLFlagTableName, "CL");
1430 }
1431
1432 cmIDEFlagTable const* cmGlobalVisualStudio10Generator::GetCSharpFlagTable()
1433   const
1434 {
1435   return LoadFlagTable(this->GetCSharpFlagTableName(),
1436                        this->DefaultCSharpFlagTableName, "CSharp");
1437 }
1438
1439 cmIDEFlagTable const* cmGlobalVisualStudio10Generator::GetRcFlagTable() const
1440 {
1441   return LoadFlagTable(this->GetRcFlagTableName(),
1442                        this->DefaultRCFlagTableName, "RC");
1443 }
1444
1445 cmIDEFlagTable const* cmGlobalVisualStudio10Generator::GetLibFlagTable() const
1446 {
1447   return LoadFlagTable(this->GetLibFlagTableName(),
1448                        this->DefaultLibFlagTableName, "LIB");
1449 }
1450
1451 cmIDEFlagTable const* cmGlobalVisualStudio10Generator::GetLinkFlagTable() const
1452 {
1453   return LoadFlagTable(this->GetLinkFlagTableName(),
1454                        this->DefaultLinkFlagTableName, "Link");
1455 }
1456
1457 cmIDEFlagTable const* cmGlobalVisualStudio10Generator::GetCudaFlagTable() const
1458 {
1459   return LoadFlagTable(std::string(), this->DefaultCudaFlagTableName, "Cuda");
1460 }
1461
1462 cmIDEFlagTable const* cmGlobalVisualStudio10Generator::GetCudaHostFlagTable()
1463   const
1464 {
1465   return LoadFlagTable(std::string(), this->DefaultCudaHostFlagTableName,
1466                        "CudaHost");
1467 }
1468
1469 cmIDEFlagTable const* cmGlobalVisualStudio10Generator::GetMasmFlagTable() const
1470 {
1471   return LoadFlagTable(this->GetMasmFlagTableName(),
1472                        this->DefaultMasmFlagTableName, "MASM");
1473 }
1474
1475 cmIDEFlagTable const* cmGlobalVisualStudio10Generator::GetNasmFlagTable() const
1476 {
1477   return LoadFlagTable(std::string(), this->DefaultNasmFlagTableName, "NASM");
1478 }
1479
1480 bool cmGlobalVisualStudio10Generator::IsMsBuildRestoreSupported() const
1481 {
1482   if (this->Version >= VSVersion::VS16) {
1483     return true;
1484   }
1485
1486   static std::string const vsVer15_7_5 = "15.7.27703.2042";
1487   cm::optional<std::string> vsVer = this->GetVSInstanceVersion();
1488   return (vsVer &&
1489           cmSystemTools::VersionCompareGreaterEq(*vsVer, vsVer15_7_5));
1490 }
1491
1492 std::string cmGlobalVisualStudio10Generator::GetClFlagTableName() const
1493 {
1494   std::string const& toolset = this->GetPlatformToolsetString();
1495   std::string const useToolset = this->CanonicalToolsetName(toolset);
1496
1497   if (toolset == "v142") {
1498     return "v142";
1499   } else if (toolset == "v141") {
1500     return "v141";
1501   } else if (useToolset == "v140") {
1502     return "v140";
1503   } else if (useToolset == "v120") {
1504     return "v12";
1505   } else if (useToolset == "v110") {
1506     return "v11";
1507   } else if (useToolset == "v100") {
1508     return "v10";
1509   } else {
1510     return "";
1511   }
1512 }
1513
1514 std::string cmGlobalVisualStudio10Generator::GetCSharpFlagTableName() const
1515 {
1516   std::string const& toolset = this->GetPlatformToolsetString();
1517   std::string const useToolset = this->CanonicalToolsetName(toolset);
1518
1519   if (useToolset == "v142") {
1520     return "v142";
1521   } else if (useToolset == "v141") {
1522     return "v141";
1523   } else if (useToolset == "v140") {
1524     return "v140";
1525   } else if (useToolset == "v120") {
1526     return "v12";
1527   } else if (useToolset == "v110") {
1528     return "v11";
1529   } else if (useToolset == "v100") {
1530     return "v10";
1531   } else {
1532     return "";
1533   }
1534 }
1535
1536 std::string cmGlobalVisualStudio10Generator::GetRcFlagTableName() const
1537 {
1538   std::string const& toolset = this->GetPlatformToolsetString();
1539   std::string const useToolset = this->CanonicalToolsetName(toolset);
1540
1541   if ((useToolset == "v140") || (useToolset == "v141") ||
1542       (useToolset == "v142")) {
1543     return "v14";
1544   } else if (useToolset == "v120") {
1545     return "v12";
1546   } else if (useToolset == "v110") {
1547     return "v11";
1548   } else if (useToolset == "v100") {
1549     return "v10";
1550   } else {
1551     return "";
1552   }
1553 }
1554
1555 std::string cmGlobalVisualStudio10Generator::GetLibFlagTableName() const
1556 {
1557   std::string const& toolset = this->GetPlatformToolsetString();
1558   std::string const useToolset = this->CanonicalToolsetName(toolset);
1559
1560   if ((useToolset == "v140") || (useToolset == "v141") ||
1561       (useToolset == "v142")) {
1562     return "v14";
1563   } else if (useToolset == "v120") {
1564     return "v12";
1565   } else if (useToolset == "v110") {
1566     return "v11";
1567   } else if (useToolset == "v100") {
1568     return "v10";
1569   } else {
1570     return "";
1571   }
1572 }
1573
1574 std::string cmGlobalVisualStudio10Generator::GetLinkFlagTableName() const
1575 {
1576   std::string const& toolset = this->GetPlatformToolsetString();
1577   std::string const useToolset = this->CanonicalToolsetName(toolset);
1578
1579   if (useToolset == "v142") {
1580     return "v142";
1581   } else if (useToolset == "v141") {
1582     return "v141";
1583   } else if (useToolset == "v140") {
1584     return "v140";
1585   } else if (useToolset == "v120") {
1586     return "v12";
1587   } else if (useToolset == "v110") {
1588     return "v11";
1589   } else if (useToolset == "v100") {
1590     return "v10";
1591   } else {
1592     return "";
1593   }
1594 }
1595
1596 std::string cmGlobalVisualStudio10Generator::GetMasmFlagTableName() const
1597 {
1598   std::string const& toolset = this->GetPlatformToolsetString();
1599   std::string const useToolset = this->CanonicalToolsetName(toolset);
1600
1601   if ((useToolset == "v140") || (useToolset == "v141") ||
1602       (useToolset == "v142")) {
1603     return "v14";
1604   } else if (useToolset == "v120") {
1605     return "v12";
1606   } else if (useToolset == "v110") {
1607     return "v11";
1608   } else if (useToolset == "v100") {
1609     return "v10";
1610   } else {
1611     return "";
1612   }
1613 }
1614
1615 std::string cmGlobalVisualStudio10Generator::CanonicalToolsetName(
1616   std::string const& toolset) const
1617 {
1618   std::size_t length = toolset.length();
1619
1620   if (cmHasLiteralSuffix(toolset, "_xp")) {
1621     length -= 3;
1622   }
1623
1624   return toolset.substr(0, length);
1625 }