cmVisualStudio10TargetGenerator.cxx 63.5 KB
Newer Older
1
2
3
/*============================================================================
  CMake - Cross Platform Makefile Generator
  Copyright 2000-2009 Kitware, Inc., Insight Software Consortium
4

5
6
  Distributed under the OSI-approved BSD License (the "License");
  see accompanying file Copyright.txt for details.
7

8
9
10
11
  This software is distributed WITHOUT ANY WARRANTY; without even the
  implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  See the License for more information.
============================================================================*/
12
#include "cmVisualStudio10TargetGenerator.h"
Brad King's avatar
Brad King committed
13
#include "cmGlobalVisualStudio10Generator.h"
14
#include "cmGeneratorTarget.h"
15
16
17
18
19
20
21
#include "cmTarget.h"
#include "cmComputeLinkInformation.h"
#include "cmGeneratedFileStream.h"
#include "cmMakefile.h"
#include "cmSourceFile.h"
#include "cmVisualStudioGeneratorOptions.h"
#include "cmLocalVisualStudio7Generator.h"
22
#include "cmCustomCommandGenerator.h"
23
24
#include "cmVS10CLFlagTable.h"
#include "cmVS10LinkFlagTable.h"
Bill Hoffman's avatar
Bill Hoffman committed
25
#include "cmVS10LibFlagTable.h"
26
27
28
#include "cmVS11CLFlagTable.h"
#include "cmVS11LinkFlagTable.h"
#include "cmVS11LibFlagTable.h"
29
30
31
#include "cmVS12CLFlagTable.h"
#include "cmVS12LinkFlagTable.h"
#include "cmVS12LibFlagTable.h"
32

33
34
#include <cmsys/auto_ptr.hxx>

Brad King's avatar
Brad King committed
35
cmIDEFlagTable const* cmVisualStudio10TargetGenerator::GetClFlagTable() const
36
{
Brad King's avatar
Brad King committed
37
38
39
  cmLocalVisualStudioGenerator::VSVersion
    v = this->LocalGenerator->GetVersion();
  if(v >= cmLocalVisualStudioGenerator::VS12)
40
    { return cmVS12CLFlagTable; }
Brad King's avatar
Brad King committed
41
  else if(v == cmLocalVisualStudioGenerator::VS11)
42
    { return cmVS11CLFlagTable; }
43
44
  else
    { return cmVS10CLFlagTable; }
45
46
}

Brad King's avatar
Brad King committed
47
cmIDEFlagTable const* cmVisualStudio10TargetGenerator::GetLibFlagTable() const
48
{
Brad King's avatar
Brad King committed
49
50
51
  cmLocalVisualStudioGenerator::VSVersion
    v = this->LocalGenerator->GetVersion();
  if(v >= cmLocalVisualStudioGenerator::VS12)
52
    { return cmVS12LibFlagTable; }
Brad King's avatar
Brad King committed
53
  else if(v == cmLocalVisualStudioGenerator::VS11)
54
    { return cmVS11LibFlagTable; }
55
56
  else
    { return cmVS10LibFlagTable; }
57
58
}

Brad King's avatar
Brad King committed
59
cmIDEFlagTable const* cmVisualStudio10TargetGenerator::GetLinkFlagTable() const
60
{
Brad King's avatar
Brad King committed
61
62
63
  cmLocalVisualStudioGenerator::VSVersion
    v = this->LocalGenerator->GetVersion();
  if(v >= cmLocalVisualStudioGenerator::VS12)
64
    { return cmVS12LinkFlagTable; }
Brad King's avatar
Brad King committed
65
  else if(v == cmLocalVisualStudioGenerator::VS11)
66
    { return cmVS11LinkFlagTable; }
67
68
  else
    { return cmVS10LinkFlagTable; }
69
70
}

71
72
73
74
75
76
77
static std::string cmVS10EscapeXML(std::string arg)
{
  cmSystemTools::ReplaceString(arg, "&", "&amp;");
  cmSystemTools::ReplaceString(arg, "<", "&lt;");
  cmSystemTools::ReplaceString(arg, ">", "&gt;");
  return arg;
}
78

79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
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;
  for(std::string::iterator c = comment.begin(); c != comment.end(); ++c)
    {
    switch (*c)
      {
      case '\r': break;
      case '\n': echoable += '\t'; break;
      case '"': /* no break */
      case '|': /* no break */
      case '&': /* no break */
      case '<': /* no break */
      case '>': /* no break */
      case '^': echoable += '^'; /* no break */
      default:  echoable += *c; break;
      }
    }
  return echoable;
}

103
104
cmVisualStudio10TargetGenerator::
cmVisualStudio10TargetGenerator(cmTarget* target,
Brad King's avatar
Brad King committed
105
                                cmGlobalVisualStudio10Generator* gg)
106
107
108
{
  this->GlobalGenerator = gg;
  this->Target = target;
109
  this->GeneratorTarget = gg->GetGeneratorTarget(target);
110
  this->Makefile = target->GetMakefile();
111
  this->LocalGenerator =
112
113
    (cmLocalVisualStudio7Generator*)
    this->Makefile->GetLocalGenerator();
114
115
116
  this->Name = this->Target->GetName();
  this->GlobalGenerator->CreateGUID(this->Name.c_str());
  this->GUID = this->GlobalGenerator->GetGUID(this->Name.c_str());
Brad King's avatar
Brad King committed
117
  this->Platform = gg->GetPlatformName();
118
  this->BuildFileStream = 0;
119
120
121
122
}

cmVisualStudio10TargetGenerator::~cmVisualStudio10TargetGenerator()
{
123
124
125
126
127
  for(OptionsMap::iterator i = this->ClOptions.begin();
      i != this->ClOptions.end(); ++i)
    {
    delete i->second;
    }
128
129
130
131
132
  for(OptionsMap::iterator i = this->LinkOptions.begin();
      i != this->LinkOptions.end(); ++i)
    {
    delete i->second;
    }
133
134
135
136
  if(!this->BuildFileStream)
    {
    return;
    }
137
138
139
140
141
  if (this->BuildFileStream->Close())
    {
    this->GlobalGenerator
      ->FileReplacedDuringGenerate(this->PathToVcxproj);
    }
142
143
144
145
146
  delete this->BuildFileStream;
}

void cmVisualStudio10TargetGenerator::WritePlatformConfigTag(
  const char* tag,
147
  const std::string& config,
148
149
150
151
152
153
154
155
156
157
158
  int indentLevel,
  const char* attribute,
  const char* end,
  std::ostream* stream)

{
  if(!stream)
    {
    stream = this->BuildFileStream;
    }
  stream->fill(' ');
159
  stream->width(indentLevel*2 );
160
  (*stream ) << "";
161
  (*stream ) << "<" << tag
Bill Hoffman's avatar
Bill Hoffman committed
162
             << " Condition=\"'$(Configuration)|$(Platform)'=='";
Brad King's avatar
Brad King committed
163
  (*stream ) << config << "|" << this->Platform << "'\"";
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
  if(attribute)
    {
    (*stream ) << attribute;
    }
  // close the tag
  (*stream ) << ">";
  if(end)
    {
    (*stream ) << end;
    }
}

void cmVisualStudio10TargetGenerator::WriteString(const char* line,
                                                  int indentLevel)
{
  this->BuildFileStream->fill(' ');
  this->BuildFileStream->width(indentLevel*2 );
  // write an empty string to get the fill level indent to print
  (*this->BuildFileStream ) << "";
  (*this->BuildFileStream ) << line;
}

186
#define VS10_USER_PROPS "$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props"
187

188
void cmVisualStudio10TargetGenerator::Generate()
189
190
{
  // do not generate external ms projects
191
192
  if(this->Target->GetType() == cmTarget::INTERFACE_LIBRARY
      || this->Target->GetProperty("EXTERNAL_MSPROJECT"))
193
194
195
    {
    return;
    }
196
  // Tell the global generator the name of the project file
197
  this->Target->SetProperty("GENERATOR_FILE_NAME",this->Name.c_str());
198
199
  this->Target->SetProperty("GENERATOR_FILE_NAME_EXT",
                            ".vcxproj");
Brad King's avatar
Brad King committed
200
  if(this->Target->GetType() <= cmTarget::OBJECT_LIBRARY)
201
    {
202
203
204
205
    if(!this->ComputeClOptions())
      {
      return;
      }
206
207
208
209
    if(!this->ComputeLinkOptions())
      {
      return;
      }
210
    }
211
212
213
  cmMakefile* mf = this->Target->GetMakefile();
  std::string path =  mf->GetStartOutputDirectory();
  path += "/";
214
  path += this->Name;
215
216
217
  path += ".vcxproj";
  this->BuildFileStream =
    new cmGeneratedFileStream(path.c_str());
218
  this->PathToVcxproj = path;
219
  this->BuildFileStream->SetCopyIfDifferent(true);
220

221
222
223
  // Write the encoding header into the file
  char magic[] = {0xEF,0xBB, 0xBF};
  this->BuildFileStream->write(magic, 3);
224
225
226

  //get the tools version to use
  const std::string toolsVer(this->GlobalGenerator->GetToolsVersion());
227
  std::string project_defaults=
228
229
    "<?xml version=\"1.0\" encoding=\"" +
    this->GlobalGenerator->Encoding() + "\"?>\n";
230
231
232
233
234
235
  project_defaults.append("<Project DefaultTargets=\"Build\" ToolsVersion=\"");
  project_defaults.append(toolsVer +"\" ");
  project_defaults.append(
          "xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n");
  this->WriteString(project_defaults.c_str(),0);

236
237
238
239
240
  this->WriteProjectConfigurations();
  this->WriteString("<PropertyGroup Label=\"Globals\">\n", 1);
  this->WriteString("<ProjectGUID>", 2);
  (*this->BuildFileStream) <<  "{" << this->GUID << "}</ProjectGUID>\n";

241
242
243
244
245
246
247
248
249
  const char* vsProjectTypes =
    this->Target->GetProperty("VS_GLOBAL_PROJECT_TYPES");
  if(vsProjectTypes)
    {
    this->WriteString("<ProjectTypes>", 2);
    (*this->BuildFileStream) << cmVS10EscapeXML(vsProjectTypes) <<
      "</ProjectTypes>\n";
    }

Steven Velez's avatar
Steven Velez committed
250
251
252
253
  const char* vsProjectName = this->Target->GetProperty("VS_SCC_PROJECTNAME");
  const char* vsLocalPath = this->Target->GetProperty("VS_SCC_LOCALPATH");
  const char* vsProvider = this->Target->GetProperty("VS_SCC_PROVIDER");

254
  if( vsProjectName && vsLocalPath && vsProvider )
Steven Velez's avatar
Steven Velez committed
255
256
257
258
259
260
261
262
263
264
    {
    this->WriteString("<SccProjectName>", 2);
    (*this->BuildFileStream) << cmVS10EscapeXML(vsProjectName) <<
      "</SccProjectName>\n";
    this->WriteString("<SccLocalPath>", 2);
    (*this->BuildFileStream) << cmVS10EscapeXML(vsLocalPath) <<
      "</SccLocalPath>\n";
    this->WriteString("<SccProvider>", 2);
    (*this->BuildFileStream) << cmVS10EscapeXML(vsProvider) <<
      "</SccProvider>\n";
265
266
267
268
269
270
271
272

    const char* vsAuxPath = this->Target->GetProperty("VS_SCC_AUXPATH");
    if( vsAuxPath )
      {
      this->WriteString("<SccAuxPath>", 2);
       (*this->BuildFileStream) << cmVS10EscapeXML(vsAuxPath) <<
         "</SccAuxPath>\n";
      }
Steven Velez's avatar
Steven Velez committed
273
274
    }

275
276
277
278
279
280
281
282
283
284
285
286
287
  const char* vsGlobalKeyword =
    this->Target->GetProperty("VS_GLOBAL_KEYWORD");
  if(!vsGlobalKeyword)
    {
    this->WriteString("<Keyword>Win32Proj</Keyword>\n", 2);
    }
  else
    {
    this->WriteString("<Keyword>", 2);
    (*this->BuildFileStream) << cmVS10EscapeXML(vsGlobalKeyword) <<
      "</Keyword>\n";
    }

288
289
290
291
292
293
294
295
296
  const char* vsGlobalRootNamespace =
    this->Target->GetProperty("VS_GLOBAL_ROOTNAMESPACE");
  if(vsGlobalRootNamespace)
    {
    this->WriteString("<RootNamespace>", 2);
    (*this->BuildFileStream) << cmVS10EscapeXML(vsGlobalRootNamespace) <<
      "</RootNamespace>\n";
    }

Brad King's avatar
Brad King committed
297
298
  this->WriteString("<Platform>", 2);
  (*this->BuildFileStream) << this->Platform << "</Platform>\n";
299
300
301
302
303
304
305
  const char* projLabel = this->Target->GetProperty("PROJECT_LABEL");
  if(!projLabel)
    {
    projLabel = this->Name.c_str();
    }
  this->WriteString("<ProjectName>", 2);
  (*this->BuildFileStream) << projLabel << "</ProjectName>\n";
306
307
308
309
310
311
312
  if(const char* targetFrameworkVersion = this->Target->GetProperty(
       "VS_DOTNET_TARGET_FRAMEWORK_VERSION"))
    {
    this->WriteString("<TargetFrameworkVersion>", 2);
    (*this->BuildFileStream) << targetFrameworkVersion
                             << "</TargetFrameworkVersion>\n";
    }
313
314
315
316
317
318
319
320
  this->WriteString("</PropertyGroup>\n", 1);
  this->WriteString("<Import Project="
                    "\"$(VCTargetsPath)\\Microsoft.Cpp.Default.props\" />\n",
                    1);
  this->WriteProjectConfigurationValues();
  this->WriteString(
    "<Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.props\" />\n", 1);
  this->WriteString("<ImportGroup Label=\"ExtensionSettings\">\n", 1);
321
322
323
324
325
  if (this->GlobalGenerator->IsMasmEnabled())
    {
    this->WriteString("<Import Project=\"$(VCTargetsPath)\\"
                      "BuildCustomizations\\masm.props\" />\n", 2);
    }
326
327
  this->WriteString("</ImportGroup>\n", 1);
  this->WriteString("<ImportGroup Label=\"PropertySheets\">\n", 1);
328
329
330
  this->WriteString("<Import Project=\"" VS10_USER_PROPS "\""
                    " Condition=\"exists('" VS10_USER_PROPS "')\""
                    " Label=\"LocalAppDataPlatform\" />", 2);
331
332
333
334
335
  this->WriteString("</ImportGroup>\n", 1);
  this->WriteString("<PropertyGroup Label=\"UserMacros\" />\n", 1);
  this->WritePathAndIncrementalLinkOptions();
  this->WriteItemDefinitionGroups();
  this->WriteCustomCommands();
336
  this->WriteAllSources();
337
  this->WriteDotNetReferences();
338
  this->WriteEmbeddedResourceGroup();
339
  this->WriteWinRTReferences();
340
341
342
343
344
  this->WriteProjectReferences();
  this->WriteString(
    "<Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.targets\""
    " />\n", 1);
  this->WriteString("<ImportGroup Label=\"ExtensionTargets\">\n", 1);
345
346
347
348
349
  if (this->GlobalGenerator->IsMasmEnabled())
    {
    this->WriteString("<Import Project=\"$(VCTargetsPath)\\"
                      "BuildCustomizations\\masm.targets\" />\n", 2);
    }
350
351
352
353
354
355
  this->WriteString("</ImportGroup>\n", 1);
  this->WriteString("</Project>", 0);
  // The groups are stored in a separate file for VS 10
  this->WriteGroups();
}

356
357
void cmVisualStudio10TargetGenerator::WriteDotNetReferences()
{
358
359
360
361
362
363
364
  std::vector<std::string> references;
  if(const char* vsDotNetReferences =
     this->Target->GetProperty("VS_DOTNET_REFERENCES"))
    {
    cmSystemTools::ExpandListArgument(vsDotNetReferences, references);
    }
  if(!references.empty())
365
366
    {
    this->WriteString("<ItemGroup>\n", 1);
367
368
    for(std::vector<std::string>::iterator ri = references.begin();
        ri != references.end(); ++ri)
369
370
      {
      this->WriteString("<Reference Include=\"", 2);
371
      (*this->BuildFileStream) << cmVS10EscapeXML(*ri) << "\">\n";
372
373
374
375
376
377
378
379
380
381
      this->WriteString("<CopyLocalSatelliteAssemblies>true"
                        "</CopyLocalSatelliteAssemblies>\n", 3);
      this->WriteString("<ReferenceOutputAssembly>true"
                        "</ReferenceOutputAssembly>\n", 3);
      this->WriteString("</Reference>\n", 2);
      }
    this->WriteString("</ItemGroup>\n", 1);
    }
}

382
383
void cmVisualStudio10TargetGenerator::WriteEmbeddedResourceGroup()
{
384
  std::vector<cmSourceFile const*> resxObjs;
385
    this->GeneratorTarget->GetResxSources(resxObjs, "");
386
387
388
  if(!resxObjs.empty())
    {
    this->WriteString("<ItemGroup>\n", 1);
389
    for(std::vector<cmSourceFile const*>::const_iterator oi = resxObjs.begin();
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
        oi != resxObjs.end(); ++oi)
      {
      std::string obj = (*oi)->GetFullPath();
      this->WriteString("<EmbeddedResource Include=\"", 2);
      this->ConvertToWindowsSlash(obj);
      (*this->BuildFileStream ) << obj << "\">\n";

      this->WriteString("<DependentUpon>", 3);
      std::string hFileName = obj.substr(0, obj.find_last_of(".")) + ".h";
      (*this->BuildFileStream ) << hFileName;
      this->WriteString("</DependentUpon>\n", 3);

      std::vector<std::string> const * configs =
        this->GlobalGenerator->GetConfigurations();
      for(std::vector<std::string>::const_iterator i = configs->begin();
          i != configs->end(); ++i)
        {
        this->WritePlatformConfigTag("LogicalName", i->c_str(), 3);
        if(this->Target->GetProperty("VS_GLOBAL_ROOTNAMESPACE"))
          {
          (*this->BuildFileStream ) << "$(RootNamespace).";
          }
        (*this->BuildFileStream ) << "%(Filename)";
        (*this->BuildFileStream ) << ".resources";
        (*this->BuildFileStream ) << "</LogicalName>\n";
        }

      this->WriteString("</EmbeddedResource>\n", 2);
      }
    this->WriteString("</ItemGroup>\n", 1);
    }
}

423
424
void cmVisualStudio10TargetGenerator::WriteWinRTReferences()
{
425
426
427
428
429
430
431
  std::vector<std::string> references;
  if(const char* vsWinRTReferences =
     this->Target->GetProperty("VS_WINRT_REFERENCES"))
    {
    cmSystemTools::ExpandListArgument(vsWinRTReferences, references);
    }
  if(!references.empty())
432
433
    {
    this->WriteString("<ItemGroup>\n", 1);
434
435
    for(std::vector<std::string>::iterator ri = references.begin();
        ri != references.end(); ++ri)
436
437
      {
      this->WriteString("<Reference Include=\"", 2);
438
      (*this->BuildFileStream) << cmVS10EscapeXML(*ri) << "\">\n";
439
440
441
442
443
444
445
      this->WriteString("<IsWinMDFile>true</IsWinMDFile>\n", 3);
      this->WriteString("</Reference>\n", 2);
      }
    this->WriteString("</ItemGroup>\n", 1);
    }
}

446
447
448
449
450
451
452
453
454
455
456
457
// ConfigurationType Application, Utility StaticLibrary DynamicLibrary

void cmVisualStudio10TargetGenerator::WriteProjectConfigurations()
{
  this->WriteString("<ItemGroup Label=\"ProjectConfigurations\">\n", 1);
  std::vector<std::string> *configs =
    static_cast<cmGlobalVisualStudio7Generator *>
    (this->GlobalGenerator)->GetConfigurations();
  for(std::vector<std::string>::iterator i = configs->begin();
      i != configs->end(); ++i)
    {
    this->WriteString("<ProjectConfiguration Include=\"", 2);
Brad King's avatar
Brad King committed
458
    (*this->BuildFileStream ) <<  *i << "|" << this->Platform << "\">\n";
459
460
    this->WriteString("<Configuration>", 3);
    (*this->BuildFileStream ) <<  *i << "</Configuration>\n";
Brad King's avatar
Brad King committed
461
462
    this->WriteString("<Platform>", 3);
    (*this->BuildFileStream) << this->Platform << "</Platform>\n";
463
464
465
466
467
468
469
    this->WriteString("</ProjectConfiguration>\n", 2);
    }
  this->WriteString("</ItemGroup>\n", 1);
}

void cmVisualStudio10TargetGenerator::WriteProjectConfigurationValues()
{
470
471
  cmGlobalVisualStudio10Generator* gg =
    static_cast<cmGlobalVisualStudio10Generator*>(this->GlobalGenerator);
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
  std::vector<std::string> *configs =
    static_cast<cmGlobalVisualStudio7Generator *>
    (this->GlobalGenerator)->GetConfigurations();
  for(std::vector<std::string>::iterator i = configs->begin();
      i != configs->end(); ++i)
    {
    this->WritePlatformConfigTag("PropertyGroup",
                                 i->c_str(),
                                 1, " Label=\"Configuration\"", "\n");
    std::string configType = "<ConfigurationType>";
    switch(this->Target->GetType())
      {
      case cmTarget::SHARED_LIBRARY:
      case cmTarget::MODULE_LIBRARY:
        configType += "DynamicLibrary";
        break;
Brad King's avatar
Brad King committed
488
      case cmTarget::OBJECT_LIBRARY:
489
490
491
492
493
494
495
496
497
      case cmTarget::STATIC_LIBRARY:
        configType += "StaticLibrary";
        break;
      case cmTarget::EXECUTABLE:
        configType += "Application";
        break;
      case cmTarget::UTILITY:
        configType += "Utility";
        break;
498
499
      case cmTarget::GLOBAL_TARGET:
      case cmTarget::UNKNOWN_LIBRARY:
500
      case cmTarget::INTERFACE_LIBRARY:
501
        break;
502
503
      }
    configType += "</ConfigurationType>\n";
504
    this->WriteString(configType.c_str(), 2);
505

506
    const char* mfcFlag =
507
      this->Target->GetMakefile()->GetDefinition("CMAKE_MFC_FLAG");
508
509
510
511
    std::string mfcFlagValue = mfcFlag ? mfcFlag : "0";

    std::string useOfMfcValue = "false";
    if(mfcFlagValue == "1")
512
      {
513
      useOfMfcValue = "Static";
514
      }
515
    else if(mfcFlagValue == "2")
516
      {
517
      useOfMfcValue = "Dynamic";
518
      }
519
520
521
522
    std::string mfcLine = "<UseOfMfc>";
    mfcLine += useOfMfcValue + "</UseOfMfc>\n";
    this->WriteString(mfcLine.c_str(), 2);

523
524
    if((this->Target->GetType() <= cmTarget::OBJECT_LIBRARY &&
       this->ClOptions[*i]->UsingUnicode()) ||
525
       this->Target->GetPropertyAsBool("VS_WINRT_EXTENSIONS"))
526
527
528
      {
      this->WriteString("<CharacterSet>Unicode</CharacterSet>\n", 2);
      }
529
530
531
532
533
    else if (this->Target->GetType() <= cmTarget::MODULE_LIBRARY &&
       this->ClOptions[*i]->UsingSBCS())
      {
      this->WriteString("<CharacterSet>NotSet</CharacterSet>\n", 2);
      }
534
535
536
537
    else
      {
      this->WriteString("<CharacterSet>MultiByte</CharacterSet>\n", 2);
      }
538
539
540
541
542
543
544
    if(const char* toolset = gg->GetPlatformToolset())
      {
      std::string pts = "<PlatformToolset>";
      pts += toolset;
      pts += "</PlatformToolset>\n";
      this->WriteString(pts.c_str(), 2);
      }
545
546
    if(this->Target->GetPropertyAsBool("VS_WINRT_EXTENSIONS"))
      {
547
548
      this->WriteString("<WindowsAppContainer>true"
                        "</WindowsAppContainer>\n", 2);
549
      }
John Farrier's avatar
John Farrier committed
550

551
552
553
554
555
    this->WriteString("</PropertyGroup>\n", 1);
    }
}

void cmVisualStudio10TargetGenerator::WriteCustomCommands()
556
557
{
  this->SourcesVisited.clear();
558
  std::vector<cmSourceFile const*> customCommands;
559
  this->GeneratorTarget->GetCustomCommands(customCommands, "");
560
  for(std::vector<cmSourceFile const*>::const_iterator
561
562
        si = customCommands.begin();
      si != customCommands.end(); ++si)
563
    {
564
    this->WriteCustomCommand(*si);
565
566
567
568
    }
}

//----------------------------------------------------------------------------
569
570
void cmVisualStudio10TargetGenerator
::WriteCustomCommand(cmSourceFile const* sf)
571
572
573
574
{
  if(this->SourcesVisited.insert(sf).second)
    {
    if(std::vector<cmSourceFile*> const* depends =
575
       this->GeneratorTarget->GetSourceDepends(sf))
576
      {
577
578
579
580
581
      for(std::vector<cmSourceFile*>::const_iterator di = depends->begin();
          di != depends->end(); ++di)
        {
        this->WriteCustomCommand(*di);
        }
582
      }
583
584
585
586
587
588
589
    if(cmCustomCommand const* command = sf->GetCustomCommand())
      {
      this->WriteString("<ItemGroup>\n", 1);
      this->WriteCustomRule(sf, *command);
      this->WriteString("</ItemGroup>\n", 1);
      }
    }
590
591
}

592
void
593
cmVisualStudio10TargetGenerator::WriteCustomRule(cmSourceFile const* source,
594
                                                 cmCustomCommand const &
595
596
597
                                                 command)
{
  std::string sourcePath = source->GetFullPath();
598
599
  // VS 10 will always rebuild a custom command attached to a .rule
  // file that doesn't exist so create the file explicitly.
600
  if (source->GetPropertyAsBool("__CMAKE_RULE"))
601
602
603
    {
    if(!cmSystemTools::FileExists(sourcePath.c_str()))
      {
604
605
606
      // Make sure the path exists for the file
      std::string path = cmSystemTools::GetFilenamePath(sourcePath);
      cmSystemTools::MakeDirectory(path.c_str());
607
      cmsys::ofstream fout(sourcePath.c_str());
608
609
610
611
612
613
      if(fout)
        {
        fout << "# generated from CMake\n";
        fout.flush();
        fout.close();
        }
614
615
616
617
618
619
620
621
      else
        {
        std::string error = "Could not create file: [";
        error +=  sourcePath;
        error += "]  ";
        cmSystemTools::Error
          (error.c_str(), cmSystemTools::GetLastSystemError().c_str());
        }
622
623
624
625
626
      }
    }
  cmLocalVisualStudio7Generator* lg = this->LocalGenerator;
  std::vector<std::string> *configs =
    static_cast<cmGlobalVisualStudio7Generator *>
627
    (this->GlobalGenerator)->GetConfigurations();
628
629
630

  this->WriteSource("CustomBuild", source, ">\n");

631
632
633
  for(std::vector<std::string>::iterator i = configs->begin();
      i != configs->end(); ++i)
    {
634
635
636
    cmCustomCommandGenerator ccg(command, *i, this->Makefile);
    std::string comment = lg->ConstructComment(ccg);
    comment = cmVS10EscapeComment(comment);
637
    std::string script =
638
      cmVS10EscapeXML(lg->ConstructScript(ccg));
639
    this->WritePlatformConfigTag("Message",i->c_str(), 3);
640
    (*this->BuildFileStream ) << cmVS10EscapeXML(comment) << "</Message>\n";
641
642
643
    this->WritePlatformConfigTag("Command", i->c_str(), 3);
    (*this->BuildFileStream ) << script << "</Command>\n";
    this->WritePlatformConfigTag("AdditionalInputs", i->c_str(), 3);
644

645
    (*this->BuildFileStream ) << source->GetFullPath();
646
    for(std::vector<std::string>::const_iterator d =
647
648
          ccg.GetDepends().begin();
        d != ccg.GetDepends().end();
649
650
        ++d)
      {
651
652
653
654
655
656
      std::string dep;
      if(this->LocalGenerator->GetRealDependency(d->c_str(), i->c_str(), dep))
        {
        this->ConvertToWindowsSlash(dep);
        (*this->BuildFileStream ) << ";" << dep;
        }
657
658
659
660
      }
    (*this->BuildFileStream ) << ";%(AdditionalInputs)</AdditionalInputs>\n";
    this->WritePlatformConfigTag("Outputs", i->c_str(), 3);
    const char* sep = "";
661
    for(std::vector<std::string>::const_iterator o =
662
663
          ccg.GetOutputs().begin();
        o != ccg.GetOutputs().end();
664
665
666
667
668
669
670
        ++o)
      {
      std::string out = *o;
      this->ConvertToWindowsSlash(out);
      (*this->BuildFileStream ) << sep << out;
      sep = ";";
      }
671
    (*this->BuildFileStream ) << "</Outputs>\n";
672
673
674
675
676
677
    if(this->LocalGenerator->GetVersion() > cmLocalVisualStudioGenerator::VS10)
      {
      // VS >= 11 let us turn off linking of custom command outputs.
      this->WritePlatformConfigTag("LinkObjects", i->c_str(), 3);
      (*this->BuildFileStream ) << "false</LinkObjects>\n";
      }
678
679
680
681
    }
  this->WriteString("</CustomBuild>\n", 2);
}

682
683
684
685
686
687
688
689
690
std::string
cmVisualStudio10TargetGenerator::ConvertPath(std::string const& path,
                                             bool forceRelative)
{
  return forceRelative
    ? cmSystemTools::RelativePath(
      this->Makefile->GetCurrentOutputDirectory(), path.c_str())
    : this->LocalGenerator->Convert(path.c_str(),
                                    cmLocalGenerator::START_OUTPUT,
691
692
                                    cmLocalGenerator::UNCHANGED,
                                    /* optional = */ true);
693
694
}

695
696
697
698
699
700
701
702
703
704
705
void cmVisualStudio10TargetGenerator::ConvertToWindowsSlash(std::string& s)
{
  // first convert all of the slashes
  std::string::size_type pos = 0;
  while((pos = s.find('/', pos)) != std::string::npos)
    {
    s[pos] = '\\';
    pos++;
    }
}
void cmVisualStudio10TargetGenerator::WriteGroups()
706
{
707
  // collect up group information
708
  std::vector<cmSourceGroup> sourceGroups =
709
    this->Makefile->GetSourceGroups();
710
  std::vector<cmSourceFile*> classes;
711
712
713
714
  if (!this->Target->GetConfigCommonSourceFiles(classes))
    {
    return;
    }
715

716
  std::set<cmSourceGroup*> groupsUsed;
717
  for(std::vector<cmSourceFile*>::const_iterator s = classes.begin();
718
719
      s != classes.end(); s++)
    {
720
    cmSourceFile* sf = *s;
721
    std::string const& source = sf->GetFullPath();
722
    cmSourceGroup* sourceGroup =
723
      this->Makefile->FindSourceGroup(source.c_str(), sourceGroups);
724
    groupsUsed.insert(sourceGroup);
725
    }
726
727
728

  this->AddMissingSourceGroups(groupsUsed, sourceGroups);

729
730
731
  // Write out group file
  std::string path =  this->Makefile->GetStartOutputDirectory();
  path += "/";
732
  path += this->Name;
733
734
  path += ".vcxproj.filters";
  cmGeneratedFileStream fout(path.c_str());
735
  fout.SetCopyIfDifferent(true);
736
737
738
739
  char magic[] = {0xEF,0xBB, 0xBF};
  fout.write(magic, 3);
  cmGeneratedFileStream* save = this->BuildFileStream;
  this->BuildFileStream = & fout;
740
741
742

  //get the tools version to use
  const std::string toolsVer(this->GlobalGenerator->GetToolsVersion());
743
  std::string project_defaults=
744
745
    "<?xml version=\"1.0\" encoding=\"" +
    this->GlobalGenerator->Encoding() + "\"?>\n";
746
747
748
749
750
751
  project_defaults.append("<Project ToolsVersion=\"");
  project_defaults.append(toolsVer +"\" ");
  project_defaults.append(
        "xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n");
  this->WriteString(project_defaults.c_str(),0);

752
753
754
755
756
  for(ToolSourceMap::const_iterator ti = this->Tools.begin();
      ti != this->Tools.end(); ++ti)
    {
    this->WriteGroupSources(ti->first.c_str(), ti->second, sourceGroups);
    }
757

758
  std::vector<cmSourceFile const*> resxObjs;
759
    this->GeneratorTarget->GetResxSources(resxObjs, "");
John Farrier's avatar
John Farrier committed
760
761
762
  if(!resxObjs.empty())
    {
    this->WriteString("<ItemGroup>\n", 1);
763
    for(std::vector<cmSourceFile const*>::const_iterator oi = resxObjs.begin();
John Farrier's avatar
John Farrier committed
764
765
766
767
768
769
770
771
772
773
774
775
        oi != resxObjs.end(); ++oi)
      {
      std::string obj = (*oi)->GetFullPath();
      this->WriteString("<EmbeddedResource Include=\"", 2);
      this->ConvertToWindowsSlash(obj);
      (*this->BuildFileStream ) << obj << "\">\n";
      this->WriteString("<Filter>Resource Files</Filter>\n", 3);
      this->WriteString("</EmbeddedResource>\n", 2);
      }
    this->WriteString("</ItemGroup>\n", 1);
    }

776
777
  // Add object library contents as external objects.
  std::vector<std::string> objs;
778
  this->GeneratorTarget->UseObjectLibraries(objs, "");
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
  if(!objs.empty())
    {
    this->WriteString("<ItemGroup>\n", 1);
    for(std::vector<std::string>::const_iterator
          oi = objs.begin(); oi != objs.end(); ++oi)
      {
      std::string obj = *oi;
      this->WriteString("<Object Include=\"", 2);
      this->ConvertToWindowsSlash(obj);
      (*this->BuildFileStream ) << obj << "\">\n";
      this->WriteString("<Filter>Object Libraries</Filter>\n", 3);
      this->WriteString("</Object>\n", 2);
      }
    this->WriteString("</ItemGroup>\n", 1);
    }

795
796
797
798
799
800
801
802
803
804
805
806
807
808
  this->WriteString("<ItemGroup>\n", 1);
  for(std::set<cmSourceGroup*>::iterator g = groupsUsed.begin();
      g != groupsUsed.end(); ++g)
    {
    cmSourceGroup* sg = *g;
    const char* name = sg->GetFullName();
    if(strlen(name) != 0)
      {
      this->WriteString("<Filter Include=\"", 2);
      (*this->BuildFileStream) << name << "\">\n";
      std::string guidName = "SG_Filter_";
      guidName += name;
      this->GlobalGenerator->CreateGUID(guidName.c_str());
      this->WriteString("<UniqueIdentifier>", 3);
809
      std::string guid
810
        = this->GlobalGenerator->GetGUID(guidName.c_str());
811
      (*this->BuildFileStream)
812
813
814
815
816
817
        << "{"
        << guid << "}"
        << "</UniqueIdentifier>\n";
      this->WriteString("</Filter>\n", 2);
      }
    }
818
819
820
821
822
823
824
825
826
827
828
829
  if(!objs.empty())
    {
    this->WriteString("<Filter Include=\"Object Libraries\">\n", 2);
    std::string guidName = "SG_Filter_Object Libraries";
    this->GlobalGenerator->CreateGUID(guidName.c_str());
    this->WriteString("<UniqueIdentifier>", 3);
    std::string guid =
      this->GlobalGenerator->GetGUID(guidName.c_str());
    (*this->BuildFileStream) << "{" << guid << "}"
                             << "</UniqueIdentifier>\n";
    this->WriteString("</Filter>\n", 2);
    }
John Farrier's avatar
John Farrier committed
830

831
  if(!resxObjs.empty())
John Farrier's avatar
John Farrier committed
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
    {
    this->WriteString("<Filter Include=\"Resource Files\">\n", 2);
    std::string guidName = "SG_Filter_Resource Files";
    this->GlobalGenerator->CreateGUID(guidName.c_str());
    this->WriteString("<UniqueIdentifier>", 3);
    std::string guid =
      this->GlobalGenerator->GetGUID(guidName.c_str());
    (*this->BuildFileStream) << "{" << guid << "}"
                             << "</UniqueIdentifier>\n";
    this->WriteString("<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;", 3);
    (*this->BuildFileStream) << "gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;";
    (*this->BuildFileStream) << "mfcribbon-ms</Extensions>\n";
    this->WriteString("</Filter>\n", 2);
  }

847
848
849
850
  this->WriteString("</ItemGroup>\n", 1);
  this->WriteString("</Project>\n", 0);
  // restore stream pointer
  this->BuildFileStream = save;
851
852
853
854
855

  if (fout.Close())
    {
    this->GlobalGenerator->FileReplacedDuringGenerate(path);
    }
856
857
}

858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
// Add to groupsUsed empty source groups that have non-empty children.
void
cmVisualStudio10TargetGenerator::AddMissingSourceGroups(
  std::set<cmSourceGroup*>& groupsUsed,
  const std::vector<cmSourceGroup>& allGroups
  )
{
  for(std::vector<cmSourceGroup>::const_iterator current = allGroups.begin();
      current != allGroups.end(); ++current)
    {
    std::vector<cmSourceGroup> const& children = current->GetGroupChildren();
    if(children.empty())
      {
      continue; // the group is really empty
      }

    this->AddMissingSourceGroups(groupsUsed, children);

    cmSourceGroup* current_ptr = const_cast<cmSourceGroup*>(&(*current));
    if(groupsUsed.find(current_ptr) != groupsUsed.end())
      {
      continue; // group has already been added to set
      }

    // check if it least one of the group's descendants is not empty
    // (at least one child must already have been added)
    std::vector<cmSourceGroup>::const_iterator child_it = children.begin();
    while(child_it != children.end())
      {
      cmSourceGroup* child_ptr = const_cast<cmSourceGroup*>(&(*child_it));
      if(groupsUsed.find(child_ptr) != groupsUsed.end())
        {
        break; // found a child that was already added => add current group too
        }
      child_it++;
      }

    if(child_it == children.end())
      {
      continue; // no descendants have source files => ignore this group
      }

    groupsUsed.insert(current_ptr);
    }
}

904
void
905
906
cmVisualStudio10TargetGenerator::
WriteGroupSources(const char* name,
907
                  ToolSources const& sources,
908
909
910
                  std::vector<cmSourceGroup>& sourceGroups)
{
  this->WriteString("<ItemGroup>\n", 1);
911
  for(ToolSources::const_iterator s = sources.begin();
912
913
      s != sources.end(); ++s)
    {
914
    cmSourceFile const* sf = s->SourceFile;
915
    std::string const& source = sf->GetFullPath();
916
    cmSourceGroup* sourceGroup =
917
      this->Makefile->FindSourceGroup(source.c_str(), sourceGroups);
918
    const char* filter = sourceGroup->GetFullName();
919
    this->WriteString("<", 2);
920
    std::string path = this->ConvertPath(source, s->RelativePath);
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
    this->ConvertToWindowsSlash(path);
    (*this->BuildFileStream) << name << " Include=\""
                             << path;
    if(strlen(filter))
      {
      (*this->BuildFileStream) << "\">\n";
      this->WriteString("<Filter>", 3);
      (*this->BuildFileStream) << filter << "</Filter>\n";
      this->WriteString("</", 2);
      (*this->BuildFileStream) << name << ">\n";
      }
    else
      {
      (*this->BuildFileStream) << "\" />\n";
      }
    }
  this->WriteString("</ItemGroup>\n", 1);
}
939

940
void cmVisualStudio10TargetGenerator::WriteSource(
941
  const char* tool, cmSourceFile const* sf, const char* end)
942
{
943
944
945
946
947
  // Visual Studio tools append relative paths to the current dir, as in:
  //
  //  c:\path\to\current\dir\..\..\..\relative\path\to\source.c
  //
  // and fail if this exceeds the maximum allowed path length.  Our path
948
  // conversion uses full paths when possible to allow deeper trees.
949
950
951
952
953
954
955
956
  bool forceRelative = false;
  std::string sourceFile = this->ConvertPath(sf->GetFullPath(), false);
  if(this->LocalGenerator->GetVersion() == cmLocalVisualStudioGenerator::VS10
     && cmSystemTools::FileIsFullPath(sourceFile.c_str()))
    {
    // Normal path conversion resulted in a full path.  VS 10 (but not 11)
    // refuses to show the property page in the IDE for a source file with a
    // full path (not starting in a '.' or '/' AFAICT).  CMake <= 2.8.4 used a
957
958
959
960
961
962
963
964
965
966
    // relative path but to allow deeper build trees CMake 2.8.[5678] used a
    // full path except for custom commands.  Custom commands do not work
    // without a relative path, but they do not seem to be involved in tools
    // with the above behavior.  For other sources we now use a relative path
    // when the combined path will not be too long so property pages appear.
    std::string sourceRel = this->ConvertPath(sf->GetFullPath(), true);
    size_t const maxLen = 250;
    if(sf->GetCustomCommand() ||
       ((strlen(this->Makefile->GetCurrentOutputDirectory()) + 1 +
         sourceRel.length()) <= maxLen))
967
968
      {
      forceRelative = true;
969
970
971
972
973
      sourceFile = sourceRel;
      }
    else
      {
      this->GlobalGenerator->PathTooLong(this->Target, sf, sourceRel);
974
975
      }
    }
976
977
  this->ConvertToWindowsSlash(sourceFile);
  this->WriteString("<", 2);
John Farrier's avatar
John Farrier committed
978
979
980
981
982
983
984
985
986
987
988
989
990
991
  (*this->BuildFileStream ) << tool << " Include=\"" << sourceFile << "\"";

  if(sf->GetExtension() == "h" &&
    this->IsResxHeader(sf->GetFullPath()))
    {
      (*this->BuildFileStream ) << ">\n";
      this->WriteString("<FileType>CppForm</FileType>\n", 3);
      this->WriteString("</ClInclude>\n", 2);
    }
  else
    {
      (*this->BuildFileStream ) << (end? end : " />\n");
    }

992
  ToolSource toolSource = {sf, forceRelative};
993
  this->Tools[tool].push_back(toolSource);
994
995
996
}

void cmVisualStudio10TargetGenerator::WriteSources(
997
  const char* tool, std::vector<cmSourceFile const*> const& sources)
998
{
999
  for(std::vector<cmSourceFile const*>::const_iterator
1000
        si = sources.begin(); si != sources.end(); ++si)
1001
    {
1002
    this->WriteSource(tool, *si);
1003
1004
1005
    }
}

1006
void cmVisualStudio10TargetGenerator::WriteAllSources()
1007
{
1008
  if(this->Target->GetType() > cmTarget::UTILITY)
1009
    {
1010
    return;
1011
    }
1012
  this->WriteString("<ItemGroup>\n", 1);
1013

1014
  std::vector<cmSourceFile const*> headerSources;
1015
  this->GeneratorTarget->GetHeaderSources(headerSources, "");
1016
  this->WriteSources("ClInclude", headerSources);
1017
  std::vector<cmSourceFile const*> idlSources;
1018
  this->GeneratorTarget->GetIDLSources(idlSources, "");
1019
  this->WriteSources("Midl", idlSources);
1020

1021
  std::vector<cmSourceFile const*> objectSources;
1022
  this->GeneratorTarget->GetObjectSources(objectSources, "");
1023
  for(std::vector<cmSourceFile const*>::const_iterator
1024
1025
        si = objectSources.begin();
      si != objectSources.end(); ++si)
1026
    {
1027
    const std::string& lang = (*si)->GetLanguage();
1028
    const char* tool = NULL;
1029
    if (lang == "C"|| lang == "CXX")
1030
1031
1032
      {
      tool = "ClCompile";
      }
1033
    else if (lang == "ASM_NASM" &&
1034
1035
1036
1037
             this->GlobalGenerator->IsMasmEnabled())
      {
      tool = "MASM";
      }
1038
    else if (lang == "RC")
1039
      {
1040
      tool = "ResourceCompile";
1041
      }
1042
1043

    if (tool)
1044
      {
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
      this->WriteSource(tool, *si, " ");
      if (this->OutputSourceSpecificFlags(*si))
        {
        this->WriteString("</", 2);
        (*this->BuildFileStream ) << tool << ">\n";
        }
      else
        {
        (*this->BuildFileStream ) << " />\n";
        }
1055
      }
1056
1057
    else
      {
1058
      this->WriteSource("None", *si);
1059
1060
      }
    }
1061

1062
  std::vector<cmSourceFile const*> externalObjects;
1063
  this->GeneratorTarget->GetExternalObjects(externalObjects, "");
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
  for(std::vector<cmSourceFile const*>::iterator
        si = externalObjects.begin();
      si != externalObjects.end(); )
    {
    if (!(*si)->GetObjectLibrary().empty())
      {
      si = externalObjects.erase(si);
      }
    else
      {
      ++si;
      }
    }
1077
1078
1079
1080
  if(this->LocalGenerator->GetVersion() > cmLocalVisualStudioGenerator::VS10)
    {
    // For VS >= 11 we use LinkObjects to avoid linking custom command
    // outputs.  Use Object for all external objects, generated or not.
1081
    this->WriteSources("Object", externalObjects);
1082
1083
    }
  else
1084
    {
1085
1086
    // If an object file is generated in this target, then vs10 will use
    // it in the build, and we have to list it as None instead of Object.
1087
    for(std::vector<cmSourceFile const*>::const_iterator
1088
1089
          si = externalObjects.begin();
        si != externalObjects.end(); ++si)
1090
      {
1091
1092
      std::vector<cmSourceFile*> const* d =
                                this->GeneratorTarget->GetSourceDepends(*si);
1093
1094
      this->WriteSource((d && !d->empty())? "None":"Object", *si);
      }
1095
1096
    }

1097
  std::vector<cmSourceFile const*> extraSources;
1098
  this->GeneratorTarget->GetExtraSources(extraSources, "");
1099
  this->WriteSources("None", extraSources);
1100

1101
1102
  // Add object library contents as external objects.
  std::vector<std::string> objs;
1103
  this->GeneratorTarget->UseObjectLibraries(objs, "");
1104
1105
1106
1107
1108
1109
1110
1111
1112
  for(std::vector<std::string>::const_iterator
        oi = objs.begin(); oi != objs.end(); ++oi)
    {
    std::string obj = *oi;
    this->WriteString("<Object Include=\"", 2);
    this->ConvertToWindowsSlash(obj);
    (*this->BuildFileStream ) << obj << "\" />\n";
    }

1113
1114
1115
1116
  this->WriteString("</ItemGroup>\n", 1);
}

bool cmVisualStudio10TargetGenerator::OutputSourceSpecificFlags(
1117
  cmSourceFile const* source)
1118
{
1119
  cmSourceFile const& sf = *source;
1120
1121

  std::string objectName;
1122
  if(this->GeneratorTarget->HasExplicitObjectName(&sf))
1123
    {
1124
    objectName = this->GeneratorTarget->GetObjectName(&sf);
1125
    }
1126
1127
1128
1129
1130
  std::string flags;
  std::string defines;
  if(const char* cflags = sf.GetProperty("COMPILE_FLAGS"))
    {
    flags += cflags;
1131
    }
1132
1133
1134
1135
  if(const char* cdefs = sf.GetProperty("COMPILE_DEFINITIONS"))
    {
    defines += cdefs;
    }
1136
  std::string lang =
Bill Hoffman's avatar