cmVisualStudio10TargetGenerator.cxx 181 KB
Newer Older
1
2
/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
   file Copyright.txt or https://cmake.org/licensing for details.  */
3
#include "cmVisualStudio10TargetGenerator.h"
Brad King's avatar
Brad King committed
4

5
6
7
8
#include <iterator>
#include <set>

#include <cm/memory>
9
#include <cm/string_view>
10
#include <cm/vector>
11
#include <cmext/algorithm>
12
13
14

#include "windows.h"

15
#include "cmComputeLinkInformation.h"
16
#include "cmCustomCommand.h"
17
#include "cmCustomCommandGenerator.h"
18
#include "cmGeneratedFileStream.h"
19
#include "cmGeneratorExpression.h"
20
21
#include "cmGeneratorTarget.h"
#include "cmGlobalVisualStudio10Generator.h"
22
#include "cmGlobalVisualStudioVersionedGenerator.h"
23
#include "cmLinkLineDeviceComputer.h"
24
#include "cmLocalVisualStudio10Generator.h"
25
26
#include "cmMakefile.h"
#include "cmSourceFile.h"
27
#include "cmStringAlgorithms.h"
28
#include "cmSystemTools.h"
29
#include "cmVisualStudioGeneratorOptions.h"
30

31
32
33
34
35
36
37
38
39
40
static void ConvertToWindowsSlash(std::string& s);

static std::string cmVS10EscapeXML(std::string arg)
{
  cmSystemTools::ReplaceString(arg, "&", "&amp;");
  cmSystemTools::ReplaceString(arg, "<", "&lt;");
  cmSystemTools::ReplaceString(arg, ">", "&gt;");
  return arg;
}

41
42
43
44
45
46
static std::string cmVS10EscapeAttr(std::string arg)
{
  cmSystemTools::ReplaceString(arg, "&", "&amp;");
  cmSystemTools::ReplaceString(arg, "<", "&lt;");
  cmSystemTools::ReplaceString(arg, ">", "&gt;");
  cmSystemTools::ReplaceString(arg, "\"", "&quot;");
47
  cmSystemTools::ReplaceString(arg, "\n", "&#10;");
48
49
50
  return arg;
}

51
52
struct cmVisualStudio10TargetGenerator::Elem
{
53
  std::ostream& S;
54
  const int Indent;
55
  bool HasElements = false;
56
  bool HasContent = false;
57
  std::string Tag;
58

59
  Elem(std::ostream& s, const std::string& tag)
60
    : S(s)
61
    , Indent(0)
62
    , Tag(tag)
63
  {
64
    this->StartElement();
65
  }
66
  Elem(const Elem&) = delete;
67
  Elem(Elem& par, cm::string_view tag)
68
69
    : S(par.S)
    , Indent(par.Indent + 1)
70
    , Tag(std::string(tag))
71
72
  {
    par.SetHasElements();
73
    this->StartElement();
74
  }
75
76
77
  void SetHasElements()
  {
    if (!HasElements) {
78
      this->S << ">";
79
80
81
      HasElements = true;
    }
  }
82
  std::ostream& WriteString(const char* line);
83
  void StartElement() { this->WriteString("<") << this->Tag; }
84
  void Element(cm::string_view tag, std::string val)
85
  {
86
    Elem(*this, tag).Content(std::move(val));
87
  }
88
  Elem& Attribute(const char* an, std::string av)
89
  {
90
    this->S << " " << an << "=\"" << cmVS10EscapeAttr(std::move(av)) << "\"";
91
92
    return *this;
  }
93
  void Content(std::string val)
94
  {
95
96
97
98
    if (!this->HasContent) {
      this->S << ">";
      this->HasContent = true;
    }
99
    this->S << cmVS10EscapeXML(std::move(val));
100
  }
101
  ~Elem()
102
  {
103
104
105
106
107
    // Do not emit element which has not been started
    if (Tag.empty()) {
      return;
    }

108
    if (HasElements) {
109
      this->WriteString("</") << this->Tag << ">";
110
    } else if (HasContent) {
111
      this->S << "</" << this->Tag << ">";
112
    } else {
113
      this->S << " />";
114
115
    }
  }
116

117
  void WritePlatformConfigTag(const std::string& tag, const std::string& cond,
118
                              const std::string& content);
119
120
};

121
122
123
class cmVS10GeneratorOptions : public cmVisualStudioGeneratorOptions
{
public:
124
  using Elem = cmVisualStudio10TargetGenerator::Elem;
125
126
127
128
129
130
131
132
  cmVS10GeneratorOptions(cmLocalVisualStudioGenerator* lg, Tool tool,
                         cmVS7FlagTable const* table,
                         cmVisualStudio10TargetGenerator* g = nullptr)
    : cmVisualStudioGeneratorOptions(lg, tool, table)
    , TargetGenerator(g)
  {
  }

133
134
  void OutputFlag(std::ostream& /*fout*/, int /*indent*/,
                  const std::string& tag, const std::string& content) override
135
136
137
138
  {
    if (!this->GetConfiguration().empty()) {
      // if there are configuration specific flags, then
      // use the configuration specific tag for PreprocessorDefinitions
139
140
141
      const std::string cond =
        this->TargetGenerator->CalcCondition(this->GetConfiguration());
      this->Parent->WritePlatformConfigTag(tag, cond, content);
142
    } else {
143
      this->Parent->Element(tag, content);
144
145
146
147
    }
  }

private:
148
149
150
  cmVisualStudio10TargetGenerator* const TargetGenerator;
  Elem* Parent = nullptr;
  friend cmVisualStudio10TargetGenerator::OptionsHelper;
151
152
};

153
struct cmVisualStudio10TargetGenerator::OptionsHelper
154
{
155
156
157
158
159
160
161
  cmVS10GeneratorOptions& O;
  OptionsHelper(cmVS10GeneratorOptions& o, Elem& e)
    : O(o)
  {
    O.Parent = &e;
  }
  ~OptionsHelper() { O.Parent = nullptr; }
162

163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
  void OutputPreprocessorDefinitions(const std::string& lang)
  {
    O.OutputPreprocessorDefinitions(O.Parent->S, O.Parent->Indent + 1, lang);
  }
  void OutputAdditionalIncludeDirectories(const std::string& lang)
  {
    O.OutputAdditionalIncludeDirectories(O.Parent->S, O.Parent->Indent + 1,
                                         lang);
  }
  void OutputFlagMap() { O.OutputFlagMap(O.Parent->S, O.Parent->Indent + 1); }
  void PrependInheritedString(std::string const& key)
  {
    O.PrependInheritedString(key);
  }
};
178

179
180
181
182
183
184
static std::string cmVS10EscapeComment(std::string comment)
{
  // MSBuild takes the CDATA of a <Message></Message> element and just
  // does "echo $CDATA" with no escapes.  We must encode the string.
  // http://technet.microsoft.com/en-us/library/cc772462%28WS.10%29.aspx
  std::string echoable;
185
186
  for (char c : comment) {
    switch (c) {
187
188
189
190
191
      case '\r':
        break;
      case '\n':
        echoable += '\t';
        break;
192
193
194
195
196
      case '"': /* no break */
      case '|': /* no break */
      case '&': /* no break */
      case '<': /* no break */
      case '>': /* no break */
197
198
      case '^':
        echoable += '^'; /* no break */
199
        CM_FALLTHROUGH;
200
      default:
201
        echoable += c;
202
        break;
203
    }
204
  }
205
206
207
  return echoable;
}

208
209
210
211
212
213
static bool cmVS10IsTargetsFile(std::string const& path)
{
  std::string const ext = cmSystemTools::GetFilenameLastExtension(path);
  return cmSystemTools::Strucmp(ext.c_str(), ".targets") == 0;
}

214
static std::string computeProjectFileExtension(cmGeneratorTarget const* t)
215
216
217
{
  std::string res;
  res = ".vcxproj";
218
  if (t->IsCSharpOnly()) {
219
220
221
222
223
    res = ".csproj";
  }
  return res;
}

224
225
cmVisualStudio10TargetGenerator::cmVisualStudio10TargetGenerator(
  cmGeneratorTarget* target, cmGlobalVisualStudio10Generator* gg)
226
227
228
229
230
231
  : GeneratorTarget(target)
  , Makefile(target->Target->GetMakefile())
  , Platform(gg->GetPlatformName())
  , Name(target->GetName())
  , GUID(gg->GetGUID(this->Name))
  , GlobalGenerator(gg)
232
233
  , LocalGenerator(
      (cmLocalVisualStudio10Generator*)target->GetLocalGenerator())
234
{
235
236
  this->Configurations =
    this->Makefile->GetGeneratorConfigs(cmMakefile::ExcludeEmptyConfig);
237
  this->NsightTegra = gg->IsNsightTegra();
238
  this->Android = gg->TargetsAndroid();
239
  for (int i = 0; i < 4; ++i) {
240
    this->NsightTegraVersion[i] = 0;
241
  }
242
243
244
  sscanf(gg->GetNsightTegraVersion().c_str(), "%u.%u.%u.%u",
         &this->NsightTegraVersion[0], &this->NsightTegraVersion[1],
         &this->NsightTegraVersion[2], &this->NsightTegraVersion[3]);
245
  this->MSTools = !this->NsightTegra && !this->Android;
246
  this->Managed = false;
247
  this->TargetCompileAsWinRT = false;
248
249
  this->IsMissingFiles = false;
  this->DefaultArtifactDir =
250
    this->LocalGenerator->GetCurrentBinaryDirectory() + "/" +
251
    this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget);
252
253
  this->InSourceBuild = (this->Makefile->GetCurrentSourceDirectory() ==
                         this->Makefile->GetCurrentBinaryDirectory());
254
  this->ClassifyAllConfigSources();
255
256
257
258
259
260
}

cmVisualStudio10TargetGenerator::~cmVisualStudio10TargetGenerator()
{
}

261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
std::string cmVisualStudio10TargetGenerator::CalcCondition(
  const std::string& config) const
{
  std::ostringstream oss;
  oss << "'$(Configuration)|$(Platform)'=='";
  oss << config << "|" << this->Platform;
  oss << "'";
  // handle special case for 32 bit C# targets
  if (this->ProjectType == csproj && this->Platform == "Win32") {
    oss << " Or ";
    oss << "'$(Configuration)|$(Platform)'=='";
    oss << config << "|x86";
    oss << "'";
  }
  return oss.str();
}

278
void cmVisualStudio10TargetGenerator::Elem::WritePlatformConfigTag(
279
  const std::string& tag, const std::string& cond, const std::string& content)
280
{
281
  Elem(*this, tag).Attribute("Condition", cond).Content(content);
282
283
}

284
std::ostream& cmVisualStudio10TargetGenerator::Elem::WriteString(
285
286
  const char* line)
{
287
  this->S << '\n';
288
289
290
291
292
293
294
295
  this->S.fill(' ');
  this->S.width(this->Indent * 2);
  // write an empty string to get the fill level indent to print
  this->S << "";
  this->S << line;
  return this->S;
}

296
297
298
299
300
#define VS10_CXX_DEFAULT_PROPS "$(VCTargetsPath)\\Microsoft.Cpp.Default.props"
#define VS10_CXX_PROPS "$(VCTargetsPath)\\Microsoft.Cpp.props"
#define VS10_CXX_USER_PROPS                                                   \
  "$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props"
#define VS10_CXX_TARGETS "$(VCTargetsPath)\\Microsoft.Cpp.targets"
301

302
303
304
305
306
307
308
309
#define VS10_CSharp_DEFAULT_PROPS                                             \
  "$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props"
// This does not seem to exist by default, it's just provided for consistency
// in case users want to have default custom props for C# targets
#define VS10_CSharp_USER_PROPS                                                \
  "$(UserRootDir)\\Microsoft.CSharp.$(Platform).user.props"
#define VS10_CSharp_TARGETS "$(MSBuildToolsPath)\\Microsoft.CSharp.targets"

310
311
312
313
314
#define VS10_CSharp_NETCF_TARGETS                                             \
  "$(MSBuildExtensionsPath)\\Microsoft\\$(TargetFrameworkIdentifier)\\"       \
  "$(TargetFrameworkTargetsVersion)\\Microsoft.$(TargetFrameworkIdentifier)"  \
  ".CSharp.targets"

315
void cmVisualStudio10TargetGenerator::Generate()
316
{
317
318
  const std::string ProjectFileExtension =
    computeProjectFileExtension(this->GeneratorTarget);
319
  if (ProjectFileExtension == ".vcxproj") {
320
321
    this->ProjectType = vcxproj;
    this->Managed = false;
322
  } else if (ProjectFileExtension == ".csproj") {
323
324
325
326
327
    if (this->GeneratorTarget->GetType() == cmStateEnums::STATIC_LIBRARY) {
      std::string message = "The C# target \"" +
        this->GeneratorTarget->GetName() +
        "\" is of type STATIC_LIBRARY. This is discouraged (and may be "
        "disabled in future). Make it a SHARED library instead.";
328
      this->Makefile->IssueMessage(MessageType::DEPRECATION_WARNING, message);
329
    }
330
331
332
    this->ProjectType = csproj;
    this->Managed = true;
  }
333
334
335
336
337
338
339

  if (this->Android &&
      this->GeneratorTarget->GetType() == cmStateEnums::EXECUTABLE &&
      !this->GeneratorTarget->Target->IsAndroidGuiExecutable()) {
    this->GlobalGenerator->AddAndroidExecutableWarning(this->Name);
  }

340
  // Tell the global generator the name of the project file
341
  this->GeneratorTarget->Target->SetProperty("GENERATOR_FILE_NAME",
342
                                             this->Name);
343
  this->GeneratorTarget->Target->SetProperty("GENERATOR_FILE_NAME_EXT",
344
                                             ProjectFileExtension);
345
346
  this->DotNetHintReferences.clear();
  this->AdditionalUsingDirectories.clear();
347
  if (this->GeneratorTarget->GetType() <= cmStateEnums::OBJECT_LIBRARY) {
348
    if (!this->ComputeClOptions()) {
349
      return;
350
351
    }
    if (!this->ComputeRcOptions()) {
352
      return;
353
    }
354
355
356
    if (!this->ComputeCudaOptions()) {
      return;
    }
357
358
359
    if (!this->ComputeCudaLinkOptions()) {
      return;
    }
360
    if (!this->ComputeMasmOptions()) {
361
      return;
362
    }
363
364
365
    if (!this->ComputeNasmOptions()) {
      return;
    }
366
    if (!this->ComputeLinkOptions()) {
367
      return;
368
    }
369
370
371
    if (!this->ComputeLibOptions()) {
      return;
    }
372
  }
373
374
375
  std::string path =
    cmStrCat(this->LocalGenerator->GetCurrentBinaryDirectory(), '/',
             this->Name, ProjectFileExtension);
376
  cmGeneratedFileStream BuildFileStream(path);
377
378
  const std::string PathToProjectFile = path;
  BuildFileStream.SetCopyIfDifferent(true);
379

380
  // Write the encoding header into the file
381
  char magic[] = { char(0xEF), char(0xBB), char(0xBF) };
382
383
  BuildFileStream.write(magic, 3);
  BuildFileStream << "<?xml version=\"1.0\" encoding=\""
384
                  << this->GlobalGenerator->Encoding() << "\"?>";
385
  {
386
    Elem e0(BuildFileStream, "Project");
387
    e0.Attribute("DefaultTargets", "Build");
388
389
390
391
392
393
394
    const char* toolsVersion = this->GlobalGenerator->GetToolsVersion();
    if (this->GlobalGenerator->GetVersion() ==
          cmGlobalVisualStudioGenerator::VS12 &&
        this->GlobalGenerator->TargetsWindowsCE()) {
      toolsVersion = "4.0";
    }
    e0.Attribute("ToolsVersion", toolsVersion);
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
    e0.Attribute("xmlns",
                 "http://schemas.microsoft.com/developer/msbuild/2003");

    if (this->NsightTegra) {
      Elem e1(e0, "PropertyGroup");
      e1.Attribute("Label", "NsightTegraProject");
      const unsigned int nsightTegraMajorVersion = this->NsightTegraVersion[0];
      const unsigned int nsightTegraMinorVersion = this->NsightTegraVersion[1];
      if (nsightTegraMajorVersion >= 2) {
        if (nsightTegraMajorVersion > 3 ||
            (nsightTegraMajorVersion == 3 && nsightTegraMinorVersion >= 1)) {
          e1.Element("NsightTegraProjectRevisionNumber", "11");
        } else {
          // Nsight Tegra 2.0 uses project revision 9.
          e1.Element("NsightTegraProjectRevisionNumber", "9");
        }
        // Tell newer versions to upgrade silently when loading.
        e1.Element("NsightTegraUpgradeOnceWithoutPrompt", "true");
413
      } else {
414
415
        // Require Nsight Tegra 1.6 for JCompile support.
        e1.Element("NsightTegraProjectRevisionNumber", "7");
416
      }
417
418
    }

419
420
421
422
423
    if (const char* hostArch =
          this->GlobalGenerator->GetPlatformToolsetHostArchitecture()) {
      Elem e1(e0, "PropertyGroup");
      e1.Element("PreferredToolArchitecture", hostArch);
    }
424

425
426
427
    if (this->ProjectType != csproj) {
      this->WriteProjectConfigurations(e0);
    }
428

429
430
431
432
    {
      Elem e1(e0, "PropertyGroup");
      e1.Attribute("Label", "Globals");
      e1.Element("ProjectGuid", "{" + this->GUID + "}");
433

434
      if ((this->MSTools || this->Android) &&
435
          this->GeneratorTarget->IsInBuildSystem()) {
436
437
438
        this->WriteApplicationTypeSettings(e1);
        this->VerifyNecessaryFiles();
      }
439

440
      cmProp vsProjectTypes =
441
442
443
444
445
446
        this->GeneratorTarget->GetProperty("VS_GLOBAL_PROJECT_TYPES");
      if (vsProjectTypes) {
        const char* tagName = "ProjectTypes";
        if (this->ProjectType == csproj) {
          tagName = "ProjectTypeGuids";
        }
447
        e1.Element(tagName, *vsProjectTypes);
448
      }
449

450
      cmProp vsProjectName =
451
        this->GeneratorTarget->GetProperty("VS_SCC_PROJECTNAME");
452
      cmProp vsLocalPath =
453
        this->GeneratorTarget->GetProperty("VS_SCC_LOCALPATH");
454
      cmProp vsProvider =
455
456
457
        this->GeneratorTarget->GetProperty("VS_SCC_PROVIDER");

      if (vsProjectName && vsLocalPath && vsProvider) {
458
459
460
        e1.Element("SccProjectName", *vsProjectName);
        e1.Element("SccLocalPath", *vsLocalPath);
        e1.Element("SccProvider", *vsProvider);
461

462
        cmProp vsAuxPath =
463
464
          this->GeneratorTarget->GetProperty("VS_SCC_AUXPATH");
        if (vsAuxPath) {
465
          e1.Element("SccAuxPath", *vsAuxPath);
466
        }
467
      }
Steven Velez's avatar
Steven Velez committed
468

469
470
471
      if (this->GeneratorTarget->GetPropertyAsBool("VS_WINRT_COMPONENT")) {
        e1.Element("WinMDAssembly", "true");
      }
472

473
      cmProp vsGlobalKeyword =
474
475
        this->GeneratorTarget->GetProperty("VS_GLOBAL_KEYWORD");
      if (!vsGlobalKeyword) {
476
477
478
479
480
        if (this->GlobalGenerator->TargetsAndroid()) {
          e1.Element("Keyword", "Android");
        } else {
          e1.Element("Keyword", "Win32Proj");
        }
481
      } else {
482
        e1.Element("Keyword", *vsGlobalKeyword);
483
      }
484

485
      cmProp vsGlobalRootNamespace =
486
487
        this->GeneratorTarget->GetProperty("VS_GLOBAL_ROOTNAMESPACE");
      if (vsGlobalRootNamespace) {
488
        e1.Element("RootNamespace", *vsGlobalRootNamespace);
489
      }
490

491
      e1.Element("Platform", this->Platform);
492
      cmProp projLabel = this->GeneratorTarget->GetProperty("PROJECT_LABEL");
493
      if (!projLabel) {
494
        projLabel = &this->Name;
495
      }
496
      e1.Element("ProjectName", *projLabel);
497
      {
498
        cmProp targetFramework =
499
500
          this->GeneratorTarget->GetProperty("DOTNET_TARGET_FRAMEWORK");
        if (targetFramework) {
501
          if (targetFramework->find(';') != std::string::npos) {
502
            e1.Element("TargetFrameworks", *targetFramework);
503
          } else {
504
            e1.Element("TargetFramework", *targetFramework);
505
506
507
          }
        } else {
          // TODO: add deprecation warning for VS_* property?
508
509
510
511
          cmProp p = this->GeneratorTarget->GetProperty(
            "VS_DOTNET_TARGET_FRAMEWORK_VERSION");
          if (!p) {
            p = this->GeneratorTarget->GetProperty(
512
513
              "DOTNET_TARGET_FRAMEWORK_VERSION");
          }
Vitaly Stakhovsky's avatar
Vitaly Stakhovsky committed
514
          const char* targetFrameworkVersion = cmToCStr(p);
515
516
517
518
519
520
521
522
523
524
          if (!targetFrameworkVersion && this->ProjectType == csproj &&
              this->GlobalGenerator->TargetsWindowsCE() &&
              this->GlobalGenerator->GetVersion() ==
                cmGlobalVisualStudioGenerator::VS12) {
            // VS12 .NETCF default to .NET framework 3.9
            targetFrameworkVersion = "v3.9";
          }
          if (targetFrameworkVersion) {
            e1.Element("TargetFrameworkVersion", targetFrameworkVersion);
          }
525
        }
526
527
528
529
530
        if (this->ProjectType == vcxproj &&
            this->GlobalGenerator->TargetsWindowsCE()) {
          e1.Element("EnableRedirectPlatform", "true");
          e1.Element("RedirectPlatformValue", this->Platform);
        }
531
532
        if (this->ProjectType == csproj &&
            this->GlobalGenerator->TargetsWindowsCE()) {
533
          cmProp targetFrameworkId = this->GeneratorTarget->GetProperty(
534
            "VS_TARGET_FRAMEWORK_IDENTIFIER");
535
536
537
538
          e1.Element("TargetFrameworkIdentifier",
                     targetFrameworkId ? *targetFrameworkId
                                       : "WindowsEmbeddedCompact");
          cmProp targetFrameworkVer = this->GeneratorTarget->GetProperty(
539
            "VS_TARGET_FRAMEWORKS_TARGET_VERSION");
540
541
          e1.Element("TargetFrameworkTargetsVersion",
                     targetFrameworkVer ? *targetFrameworkVer : "v8.0");
542
        }
543
544
545
546
547
        if (!this->GlobalGenerator->GetPlatformToolsetCudaCustomDirString()
               .empty()) {
          e1.Element(
            "CudaToolkitCustomDir",
            this->GlobalGenerator->GetPlatformToolsetCudaCustomDirString() +
548
              this->GlobalGenerator->GetPlatformToolsetCudaNvccSubdirString());
549
        }
550
      }
551

552
553
554
555
556
557
558
      // Disable the project upgrade prompt that is displayed the first time a
      // project using an older toolset version is opened in a newer version of
      // the IDE (respected by VS 2013 and above).
      if (this->GlobalGenerator->GetVersion() >=
          cmGlobalVisualStudioGenerator::VS12) {
        e1.Element("VCProjectUpgraderObjectName", "NoUpgrade");
      }
559

560
561
562
563
564
      if (const char* vcTargetsPath =
            this->GlobalGenerator->GetCustomVCTargetsPath()) {
        e1.Element("VCTargetsPath", vcTargetsPath);
      }

565
566
      std::vector<std::string> keys = this->GeneratorTarget->GetPropertyKeys();
      for (std::string const& keyIt : keys) {
567
        static const cm::string_view prefix = "VS_GLOBAL_";
568
        if (!cmHasPrefix(keyIt, prefix))
569
          continue;
570
571
        cm::string_view globalKey =
          cm::string_view(keyIt).substr(prefix.length());
572
573
574
575
576
        // Skip invalid or separately-handled properties.
        if (globalKey.empty() || globalKey == "PROJECT_TYPES" ||
            globalKey == "ROOTNAMESPACE" || globalKey == "KEYWORD") {
          continue;
        }
577
        cmProp value = this->GeneratorTarget->GetProperty(keyIt);
578
579
        if (!value)
          continue;
580
        e1.Element(globalKey, *value);
581
      }
582

583
584
585
586
587
588
589
590
591
592
593
      if (this->Managed) {
        std::string outputType;
        switch (this->GeneratorTarget->GetType()) {
          case cmStateEnums::OBJECT_LIBRARY:
          case cmStateEnums::STATIC_LIBRARY:
          case cmStateEnums::SHARED_LIBRARY:
            outputType = "Library";
            break;
          case cmStateEnums::MODULE_LIBRARY:
            outputType = "Module";
            break;
594
595
596
597
598
599
600
601
602
603
604
605
606
          case cmStateEnums::EXECUTABLE: {
            auto const win32 =
              this->GeneratorTarget->GetSafeProperty("WIN32_EXECUTABLE");
            if (win32.find("$<") != std::string::npos) {
              this->Makefile->IssueMessage(
                MessageType::FATAL_ERROR,
                cmStrCat(
                  "Target \"", this->GeneratorTarget->GetName(),
                  "\" has a generator expression in its WIN32_EXECUTABLE "
                  "property. This is not supported on managed executables."));
              return;
            }
            if (cmIsOn(win32)) {
607
608
609
610
              outputType = "WinExe";
            } else {
              outputType = "Exe";
            }
611
          } break;
612
          case cmStateEnums::UTILITY:
613
          case cmStateEnums::INTERFACE_LIBRARY:
614
615
616
617
618
619
620
621
          case cmStateEnums::GLOBAL_TARGET:
            outputType = "Utility";
            break;
          case cmStateEnums::UNKNOWN_LIBRARY:
            break;
        }
        e1.Element("OutputType", outputType);
        e1.Element("AppDesignerFolder", "Properties");
622
      }
623
    }
624

625
    switch (this->ProjectType) {
626
627
628
629
630
      case vcxproj: {
        std::string const& props =
          this->GlobalGenerator->GetPlatformToolsetVersionProps();
        if (!props.empty()) {
          Elem(e0, "Import").Attribute("Project", props);
631
        }
632
        Elem(e0, "Import").Attribute("Project", VS10_CXX_DEFAULT_PROPS);
633
      } break;
634
635
636
      case csproj:
        Elem(e0, "Import")
          .Attribute("Project", VS10_CSharp_DEFAULT_PROPS)
637
          .Attribute("Condition", "Exists('" VS10_CSharp_DEFAULT_PROPS "')");
638
639
        break;
    }
640

641
    this->WriteProjectConfigurationValues(e0);
642

643
    if (this->ProjectType == vcxproj) {
644
      Elem(e0, "Import").Attribute("Project", VS10_CXX_PROPS);
645
    }
646
647
648
649
650
651
    {
      Elem e1(e0, "ImportGroup");
      e1.Attribute("Label", "ExtensionSettings");
      e1.SetHasElements();

      if (this->GlobalGenerator->IsCudaEnabled()) {
652
653
654
655
656
        auto customDir =
          this->GlobalGenerator->GetPlatformToolsetCudaCustomDirString();
        std::string cudaPath = customDir.empty()
          ? "$(VCTargetsPath)\\BuildCustomizations\\"
          : customDir +
657
658
659
            this->GlobalGenerator
              ->GetPlatformToolsetCudaVSIntegrationSubdirString() +
            "extras\\visual_studio_integration\\MSBuildExtensions\\";
660
        Elem(e1, "Import")
661
          .Attribute("Project",
662
663
                     std::move(cudaPath) + "CUDA " +
                       this->GlobalGenerator->GetPlatformToolsetCuda() +
664
                       ".props");
665
666
667
668
      }
      if (this->GlobalGenerator->IsMasmEnabled()) {
        Elem(e1, "Import")
          .Attribute("Project",
669
                     "$(VCTargetsPath)\\BuildCustomizations\\masm.props");
670
671
672
673
674
675
      }
      if (this->GlobalGenerator->IsNasmEnabled()) {
        // Always search in the standard modules location.
        std::string propsTemplate =
          GetCMakeFilePath("Templates/MSBuild/nasm.props.in");

676
677
        std::string propsLocal =
          cmStrCat(this->DefaultArtifactDir, "\\nasm.props");
678
        ConvertToWindowsSlash(propsLocal);
679
        this->Makefile->ConfigureFile(propsTemplate, propsLocal, false, true,
680
                                      true);
681
        Elem(e1, "Import").Attribute("Project", propsLocal);
682
      }
683
    }
684
685
686
687
688
689
690
691
692
693
694
695
    {
      Elem e1(e0, "ImportGroup");
      e1.Attribute("Label", "PropertySheets");
      std::string props;
      switch (this->ProjectType) {
        case vcxproj:
          props = VS10_CXX_USER_PROPS;
          break;
        case csproj:
          props = VS10_CSharp_USER_PROPS;
          break;
      }
696
697
      if (cmProp p = this->GeneratorTarget->GetProperty("VS_USER_PROPS")) {
        props = *p;
698
699
700
701
702
703
      }
      if (!props.empty()) {
        ConvertToWindowsSlash(props);
        Elem(e1, "Import")
          .Attribute("Project", props)
          .Attribute("Condition", "exists('" + props + "')")
704
          .Attribute("Label", "LocalAppDataPlatform");
705
706
707
      }

      this->WritePlatformExtensions(e1);
708
    }
709
710

    this->WriteDotNetDocumentationFile(e0);
711
    Elem(e0, "PropertyGroup").Attribute("Label", "UserMacros");
712
713
    this->WriteWinRTPackageCertificateKeyFile(e0);
    this->WritePathAndIncrementalLinkOptions(e0);
714
    this->WriteCEDebugProjectConfigurationValues(e0);
715
716
717
718
    this->WriteItemDefinitionGroups(e0);
    this->WriteCustomCommands(e0);
    this->WriteAllSources(e0);
    this->WriteDotNetReferences(e0);
719
    this->WritePackageReferences(e0);
720
    this->WriteImports(e0);
721
722
723
724
725
    this->WriteEmbeddedResourceGroup(e0);
    this->WriteXamlFilesGroup(e0);
    this->WriteWinRTReferences(e0);
    this->WriteProjectReferences(e0);
    this->WriteSDKReferences(e0);
726
727
    switch (this->ProjectType) {
      case vcxproj:
728
        Elem(e0, "Import").Attribute("Project", VS10_CXX_TARGETS);
729
730
        break;
      case csproj:
731
732
733
734
735
        if (this->GlobalGenerator->TargetsWindowsCE()) {
          Elem(e0, "Import").Attribute("Project", VS10_CSharp_NETCF_TARGETS);
        } else {
          Elem(e0, "Import").Attribute("Project", VS10_CSharp_TARGETS);
        }
736
737
        break;
    }
738

739
740
741
742
    this->WriteTargetSpecificReferences(e0);
    {
      Elem e1(e0, "ImportGroup");
      e1.Attribute("Label", "ExtensionTargets");
743
      e1.SetHasElements();
744
745
      this->WriteTargetsFileReferences(e1);
      if (this->GlobalGenerator->IsCudaEnabled()) {
746
747
748
749
750
        auto customDir =
          this->GlobalGenerator->GetPlatformToolsetCudaCustomDirString();
        std::string cudaPath = customDir.empty()
          ? "$(VCTargetsPath)\\BuildCustomizations\\"
          : customDir +
751
752
753
            this->GlobalGenerator
              ->GetPlatformToolsetCudaVSIntegrationSubdirString() +
            "extras\\visual_studio_integration\\MSBuildExtensions\\";
754
755
        Elem(e1, "Import")
          .Attribute("Project",
756
757
                     std::move(cudaPath) + "CUDA " +
                       this->GlobalGenerator->GetPlatformToolsetCuda() +
758
                       ".targets");
759
760
761
762
      }
      if (this->GlobalGenerator->IsMasmEnabled()) {
        Elem(e1, "Import")
          .Attribute("Project",
763
                     "$(VCTargetsPath)\\BuildCustomizations\\masm.targets");
764
765
766
767
      }
      if (this->GlobalGenerator->IsNasmEnabled()) {
        std::string nasmTargets =
          GetCMakeFilePath("Templates/MSBuild/nasm.targets");
768
        Elem(e1, "Import").Attribute("Project", nasmTargets);
769
      }
770
    }
771
772
773
774
775
    if (this->ProjectType == vcxproj && this->HaveCustomCommandDepfile) {
      std::string depfileTargets =
        GetCMakeFilePath("Templates/MSBuild/CustomBuildDepFile.targets");
      Elem(e0, "Import").Attribute("Project", depfileTargets);
    }
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
    if (this->ProjectType == csproj) {
      for (std::string const& c : this->Configurations) {
        Elem e1(e0, "PropertyGroup");
        e1.Attribute("Condition", "'$(Configuration)' == '" + c + "'");
        e1.SetHasElements();
        this->WriteEvents(e1, c);
      }
      // make sure custom commands are executed before build (if necessary)
      {
        Elem e1(e0, "PropertyGroup");
        std::ostringstream oss;
        oss << "\n";
        for (std::string const& i : this->CSharpCustomCommandNames) {
          oss << "      " << i << ";\n";
        }
        oss << "      "
            << "$(BuildDependsOn)\n";
        e1.Element("BuildDependsOn", oss.str());
794
      }
795
    }
796
797
798
799
  }

  if (BuildFileStream.Close()) {
    this->GlobalGenerator->FileReplacedDuringGenerate(PathToProjectFile);
800
  }
801

802
803
804
805
  // The groups are stored in a separate file for VS 10
  this->WriteGroups();
}

806
807
808
void cmVisualStudio10TargetGenerator::WritePackageReferences(Elem& e0)
{
  std::vector<std::string> packageReferences;
809
  if (cmProp vsPackageReferences =
810
        this->GeneratorTarget->GetProperty("VS_PACKAGE_REFERENCES")) {
811
    cmExpandList(*vsPackageReferences, packageReferences);
812
813
814
815
816
817
  }
  if (!packageReferences.empty()) {
    Elem e1(e0, "ItemGroup");
    for (std::string const& ri : packageReferences) {
      size_t versionIndex = ri.find_last_of('_');
      if (versionIndex != std::string::npos) {
818
819
820
        Elem e2(e1, "PackageReference");
        e2.Attribute("Include", ri.substr(0, versionIndex));
        e2.Attribute("Version", ri.substr(versionIndex + 1));
821
822
823
824
825
      }
    }
  }
}

826
void cmVisualStudio10TargetGenerator::WriteDotNetReferences(Elem& e0)
827
{
828
  std::vector<std::string> references;
829
  if (cmProp vsDotNetReferences =
830
        this->GeneratorTarget->GetProperty("VS_DOTNET_REFERENCES")) {
831
    cmExpandList(*vsDotNetReferences, references);
832
  }
833
  cmPropertyMap const& props = this->GeneratorTarget->Target->GetProperties();
834
  for (auto const& i : props.GetList()) {
835
836
837
838
839
    static const cm::string_view vsDnRef = "VS_DOTNET_REFERENCE_";
    if (cmHasPrefix(i.first, vsDnRef)) {
      std::string path = i.second;
      if (!cmsys::SystemTools::FileIsFullPath(path)) {
        path = this->Makefile->GetCurrentSourceDirectory() + "/" + path;
840
      }
841
842
843
      ConvertToWindowsSlash(path);
      this->DotNetHintReferences[""].emplace_back(
        DotNetHintReference(i.first.substr(vsDnRef.length()), path));
844
845
    }
  }
846
  if (!references.empty() || !this->DotNetHintReferences.empty()) {
847
    Elem e1(e0, "ItemGroup");
848
    for (std::string const& ri : references) {
849
850
      // if the entry from VS_DOTNET_REFERENCES is an existing file, generate
      // a new hint-reference and name it from the filename
851
      if (cmsys::SystemTools::FileExists(ri, true)) {
852
853
        std::string name =
          cmsys::SystemTools::GetFilenameWithoutLastExtension(ri);
854
        std::string path = ri;
855
        ConvertToWindowsSlash(path);
856
        this->DotNetHintReferences[""].emplace_back(
857
          DotNetHintReference(name, path));
858
      } else {
859
        this->WriteDotNetReference(e1, ri, "", "");
860
861
      }
    }
862
863
864
865
    for (const auto& h : this->DotNetHintReferences) {
      // DotNetHintReferences is also populated from AddLibraries().
      // The configuration specific hint references are added there.
      for (const auto& i : h.second) {
866
        this->WriteDotNetReference(e1, i.first, i.second, h.first);
867
      }
868
    }
869
  }
870
871
}

872
void cmVisualStudio10TargetGenerator::WriteDotNetReference(
873
874
  Elem& e1, std::string const& ref, std::string const& hint,
  std::string const& config)
875
{
876
  Elem e2(e1, "Reference");
877
878
879
880
  // If 'config' is not empty, the reference is only added for the given
  // configuration. This is used when referencing imported managed assemblies.
  // See also cmVisualStudio10TargetGenerator::AddLibraries().
  if (!config.empty()) {
881
    e2.Attribute("Condition", this->CalcCondition(config));
882
  }
883
884
885
  e2.Attribute("Include", ref);
  e2.Element("CopyLocalSatelliteAssemblies", "true");
  e2.Element("ReferenceOutputAssembly", "true");
886
887
  if (!hint.empty()) {
    const char* privateReference = "True";
888
    if (cmProp value = this->GeneratorTarget->GetProperty(
889
          "VS_DOTNET_REFERENCES_COPY_LOCAL")) {
890
      if (cmIsOff(*value)) {
891
892
893
        privateReference = "False";
      }
    }
894
895
    e2.Element("Private", privateReference);
    e2.Element("HintPath", hint);
896
  }
897
  this->WriteDotNetReferenceCustomTags(e2, ref);
898
899
}

900
901
void cmVisualStudio10TargetGenerator::WriteImports(Elem& e0)
{
902
  cmProp imports =
903
904
    this->GeneratorTarget->Target->GetProperty("VS_PROJECT_IMPORT");
  if (imports) {
905
    std::vector<std::string> argsSplit = cmExpandedList(*imports, false);
906
907
908
909
910
911
912
913
914
915
916
    for (auto& path : argsSplit) {
      if (!cmsys::SystemTools::FileIsFullPath(path)) {
        path = this->Makefile->GetCurrentSourceDirectory() + "/" + path;
      }
      ConvertToWindowsSlash(path);
      Elem e1(e0, "Import");
      e1.Attribute("Project", path);
    }
  }
}

917
void cmVisualStudio10TargetGenerator::WriteDotNetReferenceCustomTags(
918
  Elem& e2, std::string const& ref)
919
920
921
922
923
{

  static const std::string refpropPrefix = "VS_DOTNET_REFERENCEPROP_";
  static const std::string refpropInfix = "_TAG_";
  const std::string refPropFullPrefix = refpropPrefix + ref + refpropInfix;
924
  using CustomTags = std::map<std::string, std::string>;
925
926
  CustomTags tags;
  cmPropertyMap const& props = this->GeneratorTarget->Target->GetProperties();
927
  for (const auto& i : props.GetList()) {