b291d9e756
Commit 9a6ff950 (Fix for bug where VS2010 did not use .obj files, 2011-04-01) assumed that if an external object is GENERATED that it is the output of a custom command in the current target. If it is generated by another target then VS will not automatically include the external object in the current target. This bug was preserved by the refactoring in the parent commit. Instead use <None> for external objects generated by a custom command in the current target and <Object> for all other external objects. Update the ExternalOBJ test to cover this case.
1699 lines
55 KiB
C++
1699 lines
55 KiB
C++
/*============================================================================
|
|
CMake - Cross Platform Makefile Generator
|
|
Copyright 2000-2009 Kitware, Inc., Insight Software Consortium
|
|
|
|
Distributed under the OSI-approved BSD License (the "License");
|
|
see accompanying file Copyright.txt for details.
|
|
|
|
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.
|
|
============================================================================*/
|
|
#include "cmVisualStudio10TargetGenerator.h"
|
|
#include "cmGlobalVisualStudio10Generator.h"
|
|
#include "cmGeneratorTarget.h"
|
|
#include "cmTarget.h"
|
|
#include "cmComputeLinkInformation.h"
|
|
#include "cmGeneratedFileStream.h"
|
|
#include "cmMakefile.h"
|
|
#include "cmSourceFile.h"
|
|
#include "cmVisualStudioGeneratorOptions.h"
|
|
#include "cmLocalVisualStudio7Generator.h"
|
|
#include "cmVS10CLFlagTable.h"
|
|
#include "cmVS10LinkFlagTable.h"
|
|
#include "cmVS10LibFlagTable.h"
|
|
|
|
#include <cmsys/auto_ptr.hxx>
|
|
|
|
static std::string cmVS10EscapeXML(std::string arg)
|
|
{
|
|
cmSystemTools::ReplaceString(arg, "&", "&");
|
|
cmSystemTools::ReplaceString(arg, "<", "<");
|
|
cmSystemTools::ReplaceString(arg, ">", ">");
|
|
return arg;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
cmVisualStudio10TargetGenerator::
|
|
cmVisualStudio10TargetGenerator(cmTarget* target,
|
|
cmGlobalVisualStudio10Generator* gg)
|
|
{
|
|
this->GlobalGenerator = gg;
|
|
this->Target = target;
|
|
this->GeneratorTarget = gg->GetGeneratorTarget(target);
|
|
this->Makefile = target->GetMakefile();
|
|
this->LocalGenerator =
|
|
(cmLocalVisualStudio7Generator*)
|
|
this->Makefile->GetLocalGenerator();
|
|
this->Name = this->Target->GetName();
|
|
this->GlobalGenerator->CreateGUID(this->Name.c_str());
|
|
this->GUID = this->GlobalGenerator->GetGUID(this->Name.c_str());
|
|
this->Platform = gg->GetPlatformName();
|
|
this->BuildFileStream = 0;
|
|
}
|
|
|
|
cmVisualStudio10TargetGenerator::~cmVisualStudio10TargetGenerator()
|
|
{
|
|
for(OptionsMap::iterator i = this->ClOptions.begin();
|
|
i != this->ClOptions.end(); ++i)
|
|
{
|
|
delete i->second;
|
|
}
|
|
if(!this->BuildFileStream)
|
|
{
|
|
return;
|
|
}
|
|
if (this->BuildFileStream->Close())
|
|
{
|
|
this->GlobalGenerator
|
|
->FileReplacedDuringGenerate(this->PathToVcxproj);
|
|
}
|
|
delete this->BuildFileStream;
|
|
}
|
|
|
|
void cmVisualStudio10TargetGenerator::WritePlatformConfigTag(
|
|
const char* tag,
|
|
const char* config,
|
|
int indentLevel,
|
|
const char* attribute,
|
|
const char* end,
|
|
std::ostream* stream)
|
|
|
|
{
|
|
if(!stream)
|
|
{
|
|
stream = this->BuildFileStream;
|
|
}
|
|
stream->fill(' ');
|
|
stream->width(indentLevel*2 );
|
|
(*stream ) << "";
|
|
(*stream ) << "<" << tag
|
|
<< " Condition=\"'$(Configuration)|$(Platform)'=='";
|
|
(*stream ) << config << "|" << this->Platform << "'\"";
|
|
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;
|
|
}
|
|
|
|
#define VS10_USER_PROPS "$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props"
|
|
|
|
void cmVisualStudio10TargetGenerator::Generate()
|
|
{
|
|
// do not generate external ms projects
|
|
if(this->Target->GetProperty("EXTERNAL_MSPROJECT"))
|
|
{
|
|
return;
|
|
}
|
|
// Tell the global generator the name of the project file
|
|
this->Target->SetProperty("GENERATOR_FILE_NAME",this->Name.c_str());
|
|
this->Target->SetProperty("GENERATOR_FILE_NAME_EXT",
|
|
".vcxproj");
|
|
if(this->Target->GetType() <= cmTarget::OBJECT_LIBRARY)
|
|
{
|
|
if(!this->ComputeClOptions())
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
cmMakefile* mf = this->Target->GetMakefile();
|
|
std::string path = mf->GetStartOutputDirectory();
|
|
path += "/";
|
|
path += this->Name;
|
|
path += ".vcxproj";
|
|
this->BuildFileStream =
|
|
new cmGeneratedFileStream(path.c_str());
|
|
this->PathToVcxproj = path;
|
|
this->BuildFileStream->SetCopyIfDifferent(true);
|
|
|
|
// Write the encoding header into the file
|
|
char magic[] = {0xEF,0xBB, 0xBF};
|
|
this->BuildFileStream->write(magic, 3);
|
|
this->WriteString("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n",0);
|
|
this->WriteString("<Project DefaultTargets=\"Build\" "
|
|
"ToolsVersion=\"4.0\" "
|
|
"xmlns=\"http://schemas.microsoft.com/"
|
|
"developer/msbuild/2003\">\n",
|
|
0);
|
|
this->WriteProjectConfigurations();
|
|
this->WriteString("<PropertyGroup Label=\"Globals\">\n", 1);
|
|
this->WriteString("<ProjectGUID>", 2);
|
|
(*this->BuildFileStream) << "{" << this->GUID << "}</ProjectGUID>\n";
|
|
|
|
const char* vsProjectTypes =
|
|
this->Target->GetProperty("VS_GLOBAL_PROJECT_TYPES");
|
|
if(vsProjectTypes)
|
|
{
|
|
this->WriteString("<ProjectTypes>", 2);
|
|
(*this->BuildFileStream) << cmVS10EscapeXML(vsProjectTypes) <<
|
|
"</ProjectTypes>\n";
|
|
}
|
|
|
|
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");
|
|
|
|
if( vsProjectName && vsLocalPath && vsProvider )
|
|
{
|
|
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";
|
|
|
|
const char* vsAuxPath = this->Target->GetProperty("VS_SCC_AUXPATH");
|
|
if( vsAuxPath )
|
|
{
|
|
this->WriteString("<SccAuxPath>", 2);
|
|
(*this->BuildFileStream) << cmVS10EscapeXML(vsAuxPath) <<
|
|
"</SccAuxPath>\n";
|
|
}
|
|
}
|
|
|
|
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";
|
|
}
|
|
|
|
this->WriteString("<Platform>", 2);
|
|
(*this->BuildFileStream) << this->Platform << "</Platform>\n";
|
|
const char* projLabel = this->Target->GetProperty("PROJECT_LABEL");
|
|
if(!projLabel)
|
|
{
|
|
projLabel = this->Name.c_str();
|
|
}
|
|
this->WriteString("<ProjectName>", 2);
|
|
(*this->BuildFileStream) << projLabel << "</ProjectName>\n";
|
|
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);
|
|
this->WriteString("</ImportGroup>\n", 1);
|
|
this->WriteString("<ImportGroup Label=\"PropertySheets\">\n", 1);
|
|
this->WriteString("<Import Project=\"" VS10_USER_PROPS "\""
|
|
" Condition=\"exists('" VS10_USER_PROPS "')\""
|
|
" Label=\"LocalAppDataPlatform\" />", 2);
|
|
this->WriteString("</ImportGroup>\n", 1);
|
|
this->WriteString("<PropertyGroup Label=\"UserMacros\" />\n", 1);
|
|
this->WritePathAndIncrementalLinkOptions();
|
|
this->WriteItemDefinitionGroups();
|
|
this->WriteCustomCommands();
|
|
this->WriteAllSources();
|
|
this->WriteDotNetReferences();
|
|
this->WriteWinRTReferences();
|
|
this->WriteProjectReferences();
|
|
this->WriteString(
|
|
"<Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.targets\""
|
|
" />\n", 1);
|
|
this->WriteString("<ImportGroup Label=\"ExtensionTargets\">\n", 1);
|
|
this->WriteString("</ImportGroup>\n", 1);
|
|
this->WriteString("</Project>", 0);
|
|
// The groups are stored in a separate file for VS 10
|
|
this->WriteGroups();
|
|
}
|
|
|
|
void cmVisualStudio10TargetGenerator::WriteDotNetReferences()
|
|
{
|
|
std::vector<std::string> references;
|
|
if(const char* vsDotNetReferences =
|
|
this->Target->GetProperty("VS_DOTNET_REFERENCES"))
|
|
{
|
|
cmSystemTools::ExpandListArgument(vsDotNetReferences, references);
|
|
}
|
|
if(!references.empty())
|
|
{
|
|
this->WriteString("<ItemGroup>\n", 1);
|
|
for(std::vector<std::string>::iterator ri = references.begin();
|
|
ri != references.end(); ++ri)
|
|
{
|
|
this->WriteString("<Reference Include=\"", 2);
|
|
(*this->BuildFileStream) << cmVS10EscapeXML(*ri) << "\">\n";
|
|
this->WriteString("<CopyLocalSatelliteAssemblies>true"
|
|
"</CopyLocalSatelliteAssemblies>\n", 3);
|
|
this->WriteString("<ReferenceOutputAssembly>true"
|
|
"</ReferenceOutputAssembly>\n", 3);
|
|
this->WriteString("</Reference>\n", 2);
|
|
}
|
|
this->WriteString("</ItemGroup>\n", 1);
|
|
}
|
|
}
|
|
|
|
void cmVisualStudio10TargetGenerator::WriteWinRTReferences()
|
|
{
|
|
std::vector<std::string> references;
|
|
if(const char* vsWinRTReferences =
|
|
this->Target->GetProperty("VS_WINRT_REFERENCES"))
|
|
{
|
|
cmSystemTools::ExpandListArgument(vsWinRTReferences, references);
|
|
}
|
|
if(!references.empty())
|
|
{
|
|
this->WriteString("<ItemGroup>\n", 1);
|
|
for(std::vector<std::string>::iterator ri = references.begin();
|
|
ri != references.end(); ++ri)
|
|
{
|
|
this->WriteString("<Reference Include=\"", 2);
|
|
(*this->BuildFileStream) << cmVS10EscapeXML(*ri) << "\">\n";
|
|
this->WriteString("<IsWinMDFile>true</IsWinMDFile>\n", 3);
|
|
this->WriteString("</Reference>\n", 2);
|
|
}
|
|
this->WriteString("</ItemGroup>\n", 1);
|
|
}
|
|
}
|
|
|
|
// 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);
|
|
(*this->BuildFileStream ) << *i << "|" << this->Platform << "\">\n";
|
|
this->WriteString("<Configuration>", 3);
|
|
(*this->BuildFileStream ) << *i << "</Configuration>\n";
|
|
this->WriteString("<Platform>", 3);
|
|
(*this->BuildFileStream) << this->Platform << "</Platform>\n";
|
|
this->WriteString("</ProjectConfiguration>\n", 2);
|
|
}
|
|
this->WriteString("</ItemGroup>\n", 1);
|
|
}
|
|
|
|
void cmVisualStudio10TargetGenerator::WriteProjectConfigurationValues()
|
|
{
|
|
cmGlobalVisualStudio10Generator* gg =
|
|
static_cast<cmGlobalVisualStudio10Generator*>(this->GlobalGenerator);
|
|
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;
|
|
case cmTarget::OBJECT_LIBRARY:
|
|
case cmTarget::STATIC_LIBRARY:
|
|
configType += "StaticLibrary";
|
|
break;
|
|
case cmTarget::EXECUTABLE:
|
|
configType += "Application";
|
|
break;
|
|
case cmTarget::UTILITY:
|
|
configType += "Utility";
|
|
break;
|
|
}
|
|
configType += "</ConfigurationType>\n";
|
|
this->WriteString(configType.c_str(), 2);
|
|
|
|
const char* mfcFlag =
|
|
this->Target->GetMakefile()->GetDefinition("CMAKE_MFC_FLAG");
|
|
std::string mfcFlagValue = mfcFlag ? mfcFlag : "0";
|
|
|
|
std::string useOfMfcValue = "false";
|
|
if(mfcFlagValue == "1")
|
|
{
|
|
useOfMfcValue = "Static";
|
|
}
|
|
else if(mfcFlagValue == "2")
|
|
{
|
|
useOfMfcValue = "Dynamic";
|
|
}
|
|
std::string mfcLine = "<UseOfMfc>";
|
|
mfcLine += useOfMfcValue + "</UseOfMfc>\n";
|
|
this->WriteString(mfcLine.c_str(), 2);
|
|
|
|
if(this->Target->GetType() <= cmTarget::OBJECT_LIBRARY &&
|
|
this->ClOptions[*i]->UsingUnicode() ||
|
|
this->Target->GetPropertyAsBool("VS_WINRT_EXTENSIONS"))
|
|
{
|
|
this->WriteString("<CharacterSet>Unicode</CharacterSet>\n", 2);
|
|
}
|
|
else if (this->Target->GetType() <= cmTarget::MODULE_LIBRARY &&
|
|
this->ClOptions[*i]->UsingSBCS())
|
|
{
|
|
this->WriteString("<CharacterSet>NotSet</CharacterSet>\n", 2);
|
|
}
|
|
else
|
|
{
|
|
this->WriteString("<CharacterSet>MultiByte</CharacterSet>\n", 2);
|
|
}
|
|
if(const char* toolset = gg->GetPlatformToolset())
|
|
{
|
|
std::string pts = "<PlatformToolset>";
|
|
pts += toolset;
|
|
pts += "</PlatformToolset>\n";
|
|
this->WriteString(pts.c_str(), 2);
|
|
}
|
|
if(this->Target->GetPropertyAsBool("VS_WINRT_EXTENSIONS"))
|
|
{
|
|
this->WriteString("<Immersive>true</Immersive>\n", 2);
|
|
}
|
|
this->WriteString("</PropertyGroup>\n", 1);
|
|
}
|
|
}
|
|
|
|
void cmVisualStudio10TargetGenerator::WriteCustomCommands()
|
|
{
|
|
this->SourcesVisited.clear();
|
|
for(std::vector<cmSourceFile*>::const_iterator
|
|
si = this->GeneratorTarget->CustomCommands.begin();
|
|
si != this->GeneratorTarget->CustomCommands.end(); ++si)
|
|
{
|
|
this->WriteCustomCommand(*si);
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void cmVisualStudio10TargetGenerator::WriteCustomCommand(cmSourceFile* sf)
|
|
{
|
|
if(this->SourcesVisited.insert(sf).second)
|
|
{
|
|
if(std::vector<cmSourceFile*> const* depends =
|
|
this->Target->GetSourceDepends(sf))
|
|
{
|
|
for(std::vector<cmSourceFile*>::const_iterator di = depends->begin();
|
|
di != depends->end(); ++di)
|
|
{
|
|
this->WriteCustomCommand(*di);
|
|
}
|
|
}
|
|
if(cmCustomCommand const* command = sf->GetCustomCommand())
|
|
{
|
|
this->WriteString("<ItemGroup>\n", 1);
|
|
this->WriteCustomRule(sf, *command);
|
|
this->WriteString("</ItemGroup>\n", 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
cmVisualStudio10TargetGenerator::WriteCustomRule(cmSourceFile* source,
|
|
cmCustomCommand const &
|
|
command)
|
|
{
|
|
std::string sourcePath = source->GetFullPath();
|
|
// the rule file seems to need to exist for vs10
|
|
if (source->GetExtension() == "rule")
|
|
{
|
|
if(!cmSystemTools::FileExists(sourcePath.c_str()))
|
|
{
|
|
// Make sure the path exists for the file
|
|
std::string path = cmSystemTools::GetFilenamePath(sourcePath);
|
|
cmSystemTools::MakeDirectory(path.c_str());
|
|
std::ofstream fout(sourcePath.c_str());
|
|
if(fout)
|
|
{
|
|
fout << "# generated from CMake\n";
|
|
fout.flush();
|
|
fout.close();
|
|
}
|
|
else
|
|
{
|
|
std::string error = "Could not create file: [";
|
|
error += sourcePath;
|
|
error += "] ";
|
|
cmSystemTools::Error
|
|
(error.c_str(), cmSystemTools::GetLastSystemError().c_str());
|
|
}
|
|
}
|
|
}
|
|
cmLocalVisualStudio7Generator* lg = this->LocalGenerator;
|
|
std::string comment = lg->ConstructComment(command);
|
|
comment = cmVS10EscapeComment(comment);
|
|
std::vector<std::string> *configs =
|
|
static_cast<cmGlobalVisualStudio7Generator *>
|
|
(this->GlobalGenerator)->GetConfigurations();
|
|
this->WriteString("<CustomBuild Include=\"", 2);
|
|
// custom command have to use relative paths or they do not
|
|
// show up in the GUI
|
|
std::string path = cmSystemTools::RelativePath(
|
|
this->Makefile->GetCurrentOutputDirectory(),
|
|
sourcePath.c_str());
|
|
this->ConvertToWindowsSlash(path);
|
|
(*this->BuildFileStream ) << path << "\">\n";
|
|
for(std::vector<std::string>::iterator i = configs->begin();
|
|
i != configs->end(); ++i)
|
|
{
|
|
std::string script =
|
|
cmVS10EscapeXML(lg->ConstructScript(command, i->c_str()));
|
|
this->WritePlatformConfigTag("Message",i->c_str(), 3);
|
|
(*this->BuildFileStream ) << cmVS10EscapeXML(comment) << "</Message>\n";
|
|
this->WritePlatformConfigTag("Command", i->c_str(), 3);
|
|
(*this->BuildFileStream ) << script << "</Command>\n";
|
|
this->WritePlatformConfigTag("AdditionalInputs", i->c_str(), 3);
|
|
|
|
(*this->BuildFileStream ) << source->GetFullPath();
|
|
for(std::vector<std::string>::const_iterator d =
|
|
command.GetDepends().begin();
|
|
d != command.GetDepends().end();
|
|
++d)
|
|
{
|
|
std::string dep;
|
|
if(this->LocalGenerator->GetRealDependency(d->c_str(), i->c_str(), dep))
|
|
{
|
|
this->ConvertToWindowsSlash(dep);
|
|
(*this->BuildFileStream ) << ";" << dep;
|
|
}
|
|
}
|
|
(*this->BuildFileStream ) << ";%(AdditionalInputs)</AdditionalInputs>\n";
|
|
this->WritePlatformConfigTag("Outputs", i->c_str(), 3);
|
|
const char* sep = "";
|
|
for(std::vector<std::string>::const_iterator o =
|
|
command.GetOutputs().begin();
|
|
o != command.GetOutputs().end();
|
|
++o)
|
|
{
|
|
std::string out = *o;
|
|
this->ConvertToWindowsSlash(out);
|
|
(*this->BuildFileStream ) << sep << out;
|
|
sep = ";";
|
|
}
|
|
(*this->BuildFileStream ) << "</Outputs>\n";
|
|
}
|
|
this->WriteString("</CustomBuild>\n", 2);
|
|
}
|
|
|
|
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()
|
|
{
|
|
// collect up group information
|
|
std::vector<cmSourceGroup> sourceGroups =
|
|
this->Makefile->GetSourceGroups();
|
|
std::vector<cmSourceFile*> classes = this->Target->GetSourceFiles();
|
|
|
|
std::set<cmSourceGroup*> groupsUsed;
|
|
std::vector<cmSourceFile*> clCompile;
|
|
std::vector<cmSourceFile*> customBuild;
|
|
std::vector<cmSourceFile*> none;
|
|
std::vector<cmSourceFile*> headers;
|
|
std::vector<cmSourceFile*> idls;
|
|
std::vector<cmSourceFile*> resource;
|
|
|
|
for(std::vector<cmSourceFile*>::const_iterator s = classes.begin();
|
|
s != classes.end(); s++)
|
|
{
|
|
cmSourceFile* sf = *s;
|
|
std::string const& source = sf->GetFullPath();
|
|
cmSourceGroup& sourceGroup =
|
|
this->Makefile->FindSourceGroup(source.c_str(), sourceGroups);
|
|
groupsUsed.insert(&sourceGroup);
|
|
const char* lang = sf->GetLanguage();
|
|
bool header = (*s)->GetPropertyAsBool("HEADER_FILE_ONLY")
|
|
|| this->GlobalGenerator->IgnoreFile
|
|
((*s)->GetExtension().c_str());
|
|
std::string ext =
|
|
cmSystemTools::LowerCase((*s)->GetExtension());
|
|
if(!lang)
|
|
{
|
|
lang = "None";
|
|
}
|
|
if(header)
|
|
{
|
|
headers.push_back(sf);
|
|
}
|
|
else if(lang[0] == 'C')
|
|
{
|
|
clCompile.push_back(sf);
|
|
}
|
|
else if(strcmp(lang, "RC") == 0)
|
|
{
|
|
resource.push_back(sf);
|
|
}
|
|
else if(sf->GetCustomCommand())
|
|
{
|
|
customBuild.push_back(sf);
|
|
}
|
|
else if(ext == "idl")
|
|
{
|
|
idls.push_back(sf);
|
|
}
|
|
else
|
|
{
|
|
none.push_back(sf);
|
|
}
|
|
}
|
|
|
|
this->AddMissingSourceGroups(groupsUsed, sourceGroups);
|
|
|
|
// Write out group file
|
|
std::string path = this->Makefile->GetStartOutputDirectory();
|
|
path += "/";
|
|
path += this->Name;
|
|
path += ".vcxproj.filters";
|
|
cmGeneratedFileStream fout(path.c_str());
|
|
fout.SetCopyIfDifferent(true);
|
|
char magic[] = {0xEF,0xBB, 0xBF};
|
|
fout.write(magic, 3);
|
|
cmGeneratedFileStream* save = this->BuildFileStream;
|
|
this->BuildFileStream = & fout;
|
|
this->WriteString("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
|
|
"<Project "
|
|
"ToolsVersion=\"4.0\" "
|
|
"xmlns=\"http://schemas.microsoft.com/"
|
|
"developer/msbuild/2003\">\n",
|
|
0);
|
|
this->WriteGroupSources("ClCompile", clCompile, sourceGroups);
|
|
this->WriteGroupSources("ClInclude", headers, sourceGroups);
|
|
this->WriteGroupSources("ResourceCompile", resource, sourceGroups);
|
|
this->WriteGroupSources("Midl", idls, sourceGroups);
|
|
this->WriteGroupSources("CustomBuild", customBuild, sourceGroups);
|
|
|
|
// Add object library contents as external objects.
|
|
std::vector<std::string> objs;
|
|
this->GeneratorTarget->UseObjectLibraries(objs);
|
|
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);
|
|
}
|
|
|
|
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);
|
|
std::string guid
|
|
= this->GlobalGenerator->GetGUID(guidName.c_str());
|
|
(*this->BuildFileStream)
|
|
<< "{"
|
|
<< guid << "}"
|
|
<< "</UniqueIdentifier>\n";
|
|
this->WriteString("</Filter>\n", 2);
|
|
}
|
|
}
|
|
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);
|
|
}
|
|
this->WriteString("</ItemGroup>\n", 1);
|
|
this->WriteGroupSources("None", none, sourceGroups);
|
|
this->WriteString("</Project>\n", 0);
|
|
// restore stream pointer
|
|
this->BuildFileStream = save;
|
|
|
|
if (fout.Close())
|
|
{
|
|
this->GlobalGenerator->FileReplacedDuringGenerate(path);
|
|
}
|
|
}
|
|
|
|
// 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);
|
|
}
|
|
}
|
|
|
|
void
|
|
cmVisualStudio10TargetGenerator::
|
|
WriteGroupSources(const char* name,
|
|
std::vector<cmSourceFile*> const& sources,
|
|
std::vector<cmSourceGroup>& sourceGroups)
|
|
{
|
|
this->WriteString("<ItemGroup>\n", 1);
|
|
for(std::vector<cmSourceFile*>::const_iterator s = sources.begin();
|
|
s != sources.end(); ++s)
|
|
{
|
|
cmSourceFile* sf = *s;
|
|
if(sf->GetExtension() == "obj")
|
|
{
|
|
continue;
|
|
}
|
|
std::string const& source = sf->GetFullPath();
|
|
cmSourceGroup& sourceGroup =
|
|
this->Makefile->FindSourceGroup(source.c_str(), sourceGroups);
|
|
const char* filter = sourceGroup.GetFullName();
|
|
this->WriteString("<", 2);
|
|
std::string path = source;
|
|
// custom command sources must use relative paths or they will
|
|
// not show up in the GUI.
|
|
if(sf->GetCustomCommand())
|
|
{
|
|
path = cmSystemTools::RelativePath(
|
|
this->Makefile->GetCurrentOutputDirectory(),
|
|
source.c_str());
|
|
}
|
|
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);
|
|
}
|
|
|
|
void cmVisualStudio10TargetGenerator::WriteSource(
|
|
const char* tool, cmSourceFile* sf, bool end)
|
|
{
|
|
std::string sourceFile = sf->GetFullPath();
|
|
// do not use a relative path here because it means that you
|
|
// can not use as long a path to the file.
|
|
this->ConvertToWindowsSlash(sourceFile);
|
|
this->WriteString("<", 2);
|
|
(*this->BuildFileStream ) << tool <<
|
|
" Include=\"" << sourceFile << (end? "\" />\n" : "\" ");
|
|
}
|
|
|
|
void cmVisualStudio10TargetGenerator::WriteSources(
|
|
const char* tool, std::vector<cmSourceFile*> const& sources)
|
|
{
|
|
for(std::vector<cmSourceFile*>::const_iterator
|
|
si = sources.begin(); si != sources.end(); ++si)
|
|
{
|
|
this->WriteSource(tool, *si);
|
|
}
|
|
}
|
|
|
|
void cmVisualStudio10TargetGenerator::WriteAllSources()
|
|
{
|
|
if(this->Target->GetType() > cmTarget::UTILITY)
|
|
{
|
|
return;
|
|
}
|
|
this->WriteString("<ItemGroup>\n", 1);
|
|
|
|
this->WriteSources("ClInclude", this->GeneratorTarget->HeaderSources);
|
|
this->WriteSources("Midl", this->GeneratorTarget->IDLSources);
|
|
|
|
for(std::vector<cmSourceFile*>::const_iterator
|
|
si = this->GeneratorTarget->ObjectSources.begin();
|
|
si != this->GeneratorTarget->ObjectSources.end(); ++si)
|
|
{
|
|
const char* lang = (*si)->GetLanguage();
|
|
bool cl = strcmp(lang, "C") == 0 || strcmp(lang, "CXX") == 0;
|
|
bool rc = strcmp(lang, "RC") == 0;
|
|
const char* tool = cl? "ClCompile" : (rc? "ResourceCompile" : "None");
|
|
this->WriteSource(tool, *si, false);
|
|
// ouput any flags specific to this source file
|
|
if(cl && this->OutputSourceSpecificFlags(*si))
|
|
{
|
|
// if the source file has specific flags the tag
|
|
// is ended on a new line
|
|
this->WriteString("</ClCompile>\n", 2);
|
|
}
|
|
else if(rc && this->OutputSourceSpecificFlags(*si))
|
|
{
|
|
this->WriteString("</ResourceCompile>\n", 2);
|
|
}
|
|
else
|
|
{
|
|
(*this->BuildFileStream ) << " />\n";
|
|
}
|
|
}
|
|
|
|
for(std::vector<cmSourceFile*>::const_iterator
|
|
si = this->GeneratorTarget->ExternalObjects.begin();
|
|
si != this->GeneratorTarget->ExternalObjects.end(); ++si)
|
|
{
|
|
// 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.
|
|
std::vector<cmSourceFile*> const* d = this->Target->GetSourceDepends(*si);
|
|
this->WriteSource((d && !d->empty())? "None":"Object", *si);
|
|
}
|
|
|
|
this->WriteSources("None", this->GeneratorTarget->ExtraSources);
|
|
|
|
// Add object library contents as external objects.
|
|
std::vector<std::string> objs;
|
|
this->GeneratorTarget->UseObjectLibraries(objs);
|
|
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("</ItemGroup>\n", 1);
|
|
}
|
|
|
|
bool cmVisualStudio10TargetGenerator::OutputSourceSpecificFlags(
|
|
cmSourceFile* source)
|
|
{
|
|
cmSourceFile& sf = *source;
|
|
cmLocalVisualStudio7Generator* lg = this->LocalGenerator;
|
|
|
|
std::string objectName;
|
|
if(this->GeneratorTarget->ExplicitObjectName.find(&sf)
|
|
!= this->GeneratorTarget->ExplicitObjectName.end())
|
|
{
|
|
objectName = this->GeneratorTarget->Objects[&sf];
|
|
}
|
|
std::string flags;
|
|
std::string defines;
|
|
if(const char* cflags = sf.GetProperty("COMPILE_FLAGS"))
|
|
{
|
|
flags += cflags;
|
|
}
|
|
if(const char* cdefs = sf.GetProperty("COMPILE_DEFINITIONS"))
|
|
{
|
|
defines += cdefs;
|
|
}
|
|
const char* lang =
|
|
this->GlobalGenerator->GetLanguageFromExtension
|
|
(sf.GetExtension().c_str());
|
|
const char* sourceLang = this->LocalGenerator->GetSourceFileLanguage(sf);
|
|
const char* linkLanguage = this->Target->GetLinkerLanguage();
|
|
bool needForceLang = false;
|
|
// source file does not match its extension language
|
|
if(lang && sourceLang && strcmp(lang, sourceLang) != 0)
|
|
{
|
|
needForceLang = true;
|
|
lang = sourceLang;
|
|
}
|
|
// if the source file does not match the linker language
|
|
// then force c or c++
|
|
if(needForceLang || (linkLanguage && lang
|
|
&& strcmp(lang, linkLanguage) != 0))
|
|
{
|
|
if(strcmp(lang, "CXX") == 0)
|
|
{
|
|
// force a C++ file type
|
|
flags += " /TP ";
|
|
}
|
|
else if(strcmp(lang, "C") == 0)
|
|
{
|
|
// force to c
|
|
flags += " /TC ";
|
|
}
|
|
}
|
|
bool hasFlags = false;
|
|
// for the first time we need a new line if there is something
|
|
// produced here.
|
|
const char* firstString = ">\n";
|
|
if(objectName.size())
|
|
{
|
|
(*this->BuildFileStream ) << firstString;
|
|
firstString = "";
|
|
hasFlags = true;
|
|
this->WriteString("<ObjectFileName>", 3);
|
|
(*this->BuildFileStream )
|
|
<< "$(IntDir)/" << objectName << "</ObjectFileName>\n";
|
|
}
|
|
std::vector<std::string> *configs =
|
|
static_cast<cmGlobalVisualStudio7Generator *>
|
|
(this->GlobalGenerator)->GetConfigurations();
|
|
for( std::vector<std::string>::iterator config = configs->begin();
|
|
config != configs->end(); ++config)
|
|
{
|
|
std::string configUpper = cmSystemTools::UpperCase(*config);
|
|
std::string configDefines = defines;
|
|
std::string defPropName = "COMPILE_DEFINITIONS_";
|
|
defPropName += configUpper;
|
|
if(const char* ccdefs = sf.GetProperty(defPropName.c_str()))
|
|
{
|
|
if(configDefines.size())
|
|
{
|
|
configDefines += ";";
|
|
}
|
|
configDefines += ccdefs;
|
|
}
|
|
// if we have flags or defines for this config then
|
|
// use them
|
|
if(flags.size() || configDefines.size())
|
|
{
|
|
(*this->BuildFileStream ) << firstString;
|
|
firstString = ""; // only do firstString once
|
|
hasFlags = true;
|
|
cmVisualStudioGeneratorOptions
|
|
clOptions(this->LocalGenerator,
|
|
cmVisualStudioGeneratorOptions::Compiler,
|
|
cmVS10CLFlagTable, 0, this);
|
|
clOptions.Parse(flags.c_str());
|
|
clOptions.AddDefines(configDefines.c_str());
|
|
clOptions.SetConfiguration((*config).c_str());
|
|
clOptions.OutputAdditionalOptions(*this->BuildFileStream, " ", "");
|
|
clOptions.OutputFlagMap(*this->BuildFileStream, " ");
|
|
clOptions.OutputPreprocessorDefinitions(*this->BuildFileStream,
|
|
" ", "\n", lang);
|
|
}
|
|
}
|
|
return hasFlags;
|
|
}
|
|
|
|
|
|
void cmVisualStudio10TargetGenerator::WritePathAndIncrementalLinkOptions()
|
|
{
|
|
cmTarget::TargetType ttype = this->Target->GetType();
|
|
if(ttype > cmTarget::GLOBAL_TARGET)
|
|
{
|
|
return;
|
|
}
|
|
|
|
this->WriteString("<PropertyGroup>\n", 2);
|
|
this->WriteString("<_ProjectFileVersion>10.0.20506.1"
|
|
"</_ProjectFileVersion>\n", 3);
|
|
std::vector<std::string> *configs =
|
|
static_cast<cmGlobalVisualStudio7Generator *>
|
|
(this->GlobalGenerator)->GetConfigurations();
|
|
for(std::vector<std::string>::iterator config = configs->begin();
|
|
config != configs->end(); ++config)
|
|
{
|
|
if(ttype >= cmTarget::UTILITY)
|
|
{
|
|
this->WritePlatformConfigTag("IntDir", config->c_str(), 3);
|
|
*this->BuildFileStream
|
|
<< "$(Platform)\\$(Configuration)\\$(ProjectName)\\"
|
|
<< "</IntDir>\n";
|
|
}
|
|
else
|
|
{
|
|
std::string intermediateDir = this->LocalGenerator->
|
|
GetTargetDirectory(*this->Target);
|
|
intermediateDir += "/";
|
|
intermediateDir += *config;
|
|
intermediateDir += "/";
|
|
std::string outDir;
|
|
std::string targetNameFull;
|
|
if(ttype == cmTarget::OBJECT_LIBRARY)
|
|
{
|
|
outDir = intermediateDir;
|
|
targetNameFull = this->Target->GetName();
|
|
targetNameFull += ".lib";
|
|
}
|
|
else
|
|
{
|
|
outDir = this->Target->GetDirectory(config->c_str()) + "/";
|
|
targetNameFull = this->Target->GetFullName(config->c_str());
|
|
}
|
|
this->ConvertToWindowsSlash(intermediateDir);
|
|
this->ConvertToWindowsSlash(outDir);
|
|
|
|
this->WritePlatformConfigTag("OutDir", config->c_str(), 3);
|
|
*this->BuildFileStream << outDir
|
|
<< "</OutDir>\n";
|
|
|
|
this->WritePlatformConfigTag("IntDir", config->c_str(), 3);
|
|
*this->BuildFileStream << intermediateDir
|
|
<< "</IntDir>\n";
|
|
|
|
this->WritePlatformConfigTag("TargetName", config->c_str(), 3);
|
|
*this->BuildFileStream
|
|
<< cmSystemTools::GetFilenameWithoutLastExtension(
|
|
targetNameFull.c_str())
|
|
<< "</TargetName>\n";
|
|
|
|
this->WritePlatformConfigTag("TargetExt", config->c_str(), 3);
|
|
*this->BuildFileStream
|
|
<< cmSystemTools::GetFilenameLastExtension(targetNameFull.c_str())
|
|
<< "</TargetExt>\n";
|
|
|
|
this->OutputLinkIncremental(*config);
|
|
}
|
|
}
|
|
this->WriteString("</PropertyGroup>\n", 2);
|
|
}
|
|
|
|
|
|
|
|
void
|
|
cmVisualStudio10TargetGenerator::
|
|
OutputLinkIncremental(std::string const& configName)
|
|
{
|
|
std::string CONFIG = cmSystemTools::UpperCase(configName);
|
|
// static libraries and things greater than modules do not need
|
|
// to set this option
|
|
if(this->Target->GetType() == cmTarget::STATIC_LIBRARY
|
|
|| this->Target->GetType() > cmTarget::MODULE_LIBRARY)
|
|
{
|
|
return;
|
|
}
|
|
const char* linkType = "SHARED";
|
|
if(this->Target->GetType() == cmTarget::EXECUTABLE)
|
|
{
|
|
linkType = "EXE";
|
|
}
|
|
|
|
// assume incremental linking
|
|
const char* incremental = "true";
|
|
const char* linkLanguage =
|
|
this->Target->GetLinkerLanguage(configName.c_str());
|
|
if(!linkLanguage)
|
|
{
|
|
cmSystemTools::Error
|
|
("CMake can not determine linker language for target:",
|
|
this->Name.c_str());
|
|
return;
|
|
}
|
|
std::string linkFlagVarBase = "CMAKE_";
|
|
linkFlagVarBase += linkType;
|
|
linkFlagVarBase += "_LINKER_FLAGS";
|
|
std::string flags = this->
|
|
Target->GetMakefile()->GetRequiredDefinition(linkFlagVarBase.c_str());
|
|
std::string linkFlagVar = linkFlagVarBase + "_" + CONFIG;
|
|
flags += this->
|
|
Target->GetMakefile()->GetRequiredDefinition(linkFlagVar.c_str());
|
|
if(strcmp(linkLanguage, "C") == 0 || strcmp(linkLanguage, "CXX") == 0
|
|
|| strcmp(linkLanguage, "Fortran") == 0)
|
|
{
|
|
std::string baseFlagVar = "CMAKE_";
|
|
baseFlagVar += linkLanguage;
|
|
baseFlagVar += "_FLAGS";
|
|
flags += this->
|
|
Target->GetMakefile()->GetRequiredDefinition(baseFlagVar.c_str());
|
|
std::string flagVar = baseFlagVar + std::string("_") + CONFIG;
|
|
flags +=
|
|
Target->GetMakefile()->GetRequiredDefinition(flagVar.c_str());
|
|
}
|
|
const char* targetLinkFlags = this->Target->GetProperty("LINK_FLAGS");
|
|
if(targetLinkFlags)
|
|
{
|
|
flags += " ";
|
|
flags += targetLinkFlags;
|
|
}
|
|
std::string flagsProp = "LINK_FLAGS_";
|
|
flagsProp += CONFIG;
|
|
if(const char* flagsConfig = this->Target->GetProperty(flagsProp.c_str()))
|
|
{
|
|
flags += " ";
|
|
flags += flagsConfig;
|
|
}
|
|
if(flags.find("INCREMENTAL:NO") != flags.npos)
|
|
{
|
|
incremental = "false";
|
|
}
|
|
this->WritePlatformConfigTag("LinkIncremental", configName.c_str(), 3);
|
|
*this->BuildFileStream << incremental
|
|
<< "</LinkIncremental>\n";
|
|
|
|
const char* manifest = "true";
|
|
if(flags.find("MANIFEST:NO") != flags.npos)
|
|
{
|
|
manifest = "false";
|
|
}
|
|
this->WritePlatformConfigTag("GenerateManifest", configName.c_str(), 3);
|
|
*this->BuildFileStream << manifest
|
|
<< "</GenerateManifest>\n";
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool cmVisualStudio10TargetGenerator::ComputeClOptions()
|
|
{
|
|
std::vector<std::string> const* configs =
|
|
this->GlobalGenerator->GetConfigurations();
|
|
for(std::vector<std::string>::const_iterator i = configs->begin();
|
|
i != configs->end(); ++i)
|
|
{
|
|
if(!this->ComputeClOptions(*i))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool cmVisualStudio10TargetGenerator::ComputeClOptions(
|
|
std::string const& configName)
|
|
{
|
|
// much of this was copied from here:
|
|
// copied from cmLocalVisualStudio7Generator.cxx 805
|
|
// TODO: Integrate code below with cmLocalVisualStudio7Generator.
|
|
|
|
cmsys::auto_ptr<Options> pOptions(
|
|
new Options(this->LocalGenerator, Options::Compiler,
|
|
cmVS10CLFlagTable));
|
|
Options& clOptions = *pOptions;
|
|
|
|
std::string flags;
|
|
// collect up flags for
|
|
if(this->Target->GetType() < cmTarget::UTILITY)
|
|
{
|
|
const char* linkLanguage =
|
|
this->Target->GetLinkerLanguage(configName.c_str());
|
|
if(!linkLanguage)
|
|
{
|
|
cmSystemTools::Error
|
|
("CMake can not determine linker language for target:",
|
|
this->Name.c_str());
|
|
return false;
|
|
}
|
|
if(strcmp(linkLanguage, "C") == 0 || strcmp(linkLanguage, "CXX") == 0
|
|
|| strcmp(linkLanguage, "Fortran") == 0)
|
|
{
|
|
std::string baseFlagVar = "CMAKE_";
|
|
baseFlagVar += linkLanguage;
|
|
baseFlagVar += "_FLAGS";
|
|
flags = this->
|
|
Target->GetMakefile()->GetRequiredDefinition(baseFlagVar.c_str());
|
|
std::string flagVar = baseFlagVar + std::string("_") +
|
|
cmSystemTools::UpperCase(configName);
|
|
flags += " ";
|
|
flags += this->
|
|
Target->GetMakefile()->GetRequiredDefinition(flagVar.c_str());
|
|
}
|
|
// set the correct language
|
|
if(strcmp(linkLanguage, "C") == 0)
|
|
{
|
|
flags += " /TC ";
|
|
}
|
|
if(strcmp(linkLanguage, "CXX") == 0)
|
|
{
|
|
flags += " /TP ";
|
|
}
|
|
}
|
|
// Add the target-specific flags.
|
|
if(const char* targetFlags = this->Target->GetProperty("COMPILE_FLAGS"))
|
|
{
|
|
flags += " ";
|
|
flags += targetFlags;
|
|
}
|
|
std::string configUpper = cmSystemTools::UpperCase(configName);
|
|
std::string defPropName = "COMPILE_DEFINITIONS_";
|
|
defPropName += configUpper;
|
|
|
|
// Get preprocessor definitions for this directory.
|
|
std::string defineFlags = this->Target->GetMakefile()->GetDefineFlags();
|
|
clOptions.FixExceptionHandlingDefault();
|
|
clOptions.Parse(flags.c_str());
|
|
clOptions.Parse(defineFlags.c_str());
|
|
clOptions.AddDefines
|
|
(this->Makefile->GetProperty("COMPILE_DEFINITIONS"));
|
|
clOptions.AddDefines(this->Target->GetProperty("COMPILE_DEFINITIONS"));
|
|
clOptions.AddDefines(this->Makefile->GetProperty(defPropName.c_str()));
|
|
clOptions.AddDefines(this->Target->GetProperty(defPropName.c_str()));
|
|
clOptions.SetVerboseMakefile(
|
|
this->Makefile->IsOn("CMAKE_VERBOSE_MAKEFILE"));
|
|
|
|
// Add a definition for the configuration name.
|
|
std::string configDefine = "CMAKE_INTDIR=\"";
|
|
configDefine += configName;
|
|
configDefine += "\"";
|
|
clOptions.AddDefine(configDefine);
|
|
if(const char* exportMacro = this->Target->GetExportMacro())
|
|
{
|
|
clOptions.AddDefine(exportMacro);
|
|
}
|
|
|
|
this->ClOptions[configName] = pOptions.release();
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void cmVisualStudio10TargetGenerator::WriteClOptions(
|
|
std::string const& configName,
|
|
std::vector<std::string> const& includes)
|
|
{
|
|
Options& clOptions = *(this->ClOptions[configName]);
|
|
this->WriteString("<ClCompile>\n", 2);
|
|
clOptions.OutputAdditionalOptions(*this->BuildFileStream, " ", "");
|
|
this->OutputIncludes(includes);
|
|
clOptions.OutputFlagMap(*this->BuildFileStream, " ");
|
|
|
|
// If not in debug mode, write the DebugInformationFormat field
|
|
// without value so PDBs don't get generated uselessly.
|
|
if(!clOptions.IsDebug())
|
|
{
|
|
this->WriteString("<DebugInformationFormat>"
|
|
"</DebugInformationFormat>\n", 3);
|
|
}
|
|
|
|
clOptions.OutputPreprocessorDefinitions(*this->BuildFileStream, " ",
|
|
"\n", "CXX");
|
|
this->WriteString("<AssemblerListingLocation>", 3);
|
|
*this->BuildFileStream << configName
|
|
<< "</AssemblerListingLocation>\n";
|
|
this->WriteString("<ObjectFileName>$(IntDir)</ObjectFileName>\n", 3);
|
|
if(this->Target->GetType() != cmTarget::OBJECT_LIBRARY)
|
|
{
|
|
// TODO: PDB for object library?
|
|
this->WriteString("<ProgramDataBaseFileName>", 3);
|
|
*this->BuildFileStream << this->Target->GetDirectory(configName.c_str())
|
|
<< "/"
|
|
<< this->Target->GetPDBName(configName.c_str())
|
|
<< "</ProgramDataBaseFileName>\n";
|
|
}
|
|
this->WriteString("</ClCompile>\n", 2);
|
|
}
|
|
|
|
void cmVisualStudio10TargetGenerator::
|
|
OutputIncludes(std::vector<std::string> const & includes)
|
|
{
|
|
this->WriteString("<AdditionalIncludeDirectories>", 3);
|
|
for(std::vector<std::string>::const_iterator i = includes.begin();
|
|
i != includes.end(); ++i)
|
|
{
|
|
*this->BuildFileStream << *i << ";";
|
|
}
|
|
this->WriteString("%(AdditionalIncludeDirectories)"
|
|
"</AdditionalIncludeDirectories>\n", 0);
|
|
}
|
|
|
|
|
|
|
|
void cmVisualStudio10TargetGenerator::
|
|
WriteRCOptions(std::string const& configName,
|
|
std::vector<std::string> const & includes)
|
|
{
|
|
this->WriteString("<ResourceCompile>\n", 2);
|
|
Options& clOptions = *(this->ClOptions[configName]);
|
|
clOptions.OutputPreprocessorDefinitions(*this->BuildFileStream, " ",
|
|
"\n", "RC");
|
|
this->OutputIncludes(includes);
|
|
this->WriteString("</ResourceCompile>\n", 2);
|
|
}
|
|
|
|
|
|
void
|
|
cmVisualStudio10TargetGenerator::WriteLibOptions(std::string const& config)
|
|
{
|
|
if(this->Target->GetType() != cmTarget::STATIC_LIBRARY)
|
|
{
|
|
return;
|
|
}
|
|
const char* libflags = this->Target->GetProperty("STATIC_LIBRARY_FLAGS");
|
|
std::string flagsConfigVar = "STATIC_LIBRARY_FLAGS_";
|
|
flagsConfigVar += cmSystemTools::UpperCase(config);
|
|
const char* libflagsConfig =
|
|
this->Target->GetProperty(flagsConfigVar.c_str());
|
|
if(libflags || libflagsConfig)
|
|
{
|
|
this->WriteString("<Lib>\n", 2);
|
|
cmVisualStudioGeneratorOptions
|
|
libOptions(this->LocalGenerator,
|
|
cmVisualStudioGeneratorOptions::Linker,
|
|
cmVS10LibFlagTable, 0, this);
|
|
libOptions.Parse(libflags?libflags:"");
|
|
libOptions.Parse(libflagsConfig?libflagsConfig:"");
|
|
libOptions.OutputAdditionalOptions(*this->BuildFileStream, " ", "");
|
|
libOptions.OutputFlagMap(*this->BuildFileStream, " ");
|
|
this->WriteString("</Lib>\n", 2);
|
|
}
|
|
}
|
|
|
|
|
|
void cmVisualStudio10TargetGenerator::WriteLinkOptions(std::string const&
|
|
config)
|
|
{
|
|
|
|
// static libraries and things greater than modules do not need
|
|
// to set this option
|
|
if(this->Target->GetType() == cmTarget::STATIC_LIBRARY
|
|
|| this->Target->GetType() > cmTarget::MODULE_LIBRARY)
|
|
{
|
|
return;
|
|
}
|
|
const char* linkLanguage =
|
|
this->Target->GetLinkerLanguage(config.c_str());
|
|
if(!linkLanguage)
|
|
{
|
|
cmSystemTools::Error
|
|
("CMake can not determine linker language for target:",
|
|
this->Name.c_str());
|
|
return;
|
|
}
|
|
|
|
this->WriteString("<Link>\n", 2);
|
|
std::string CONFIG = cmSystemTools::UpperCase(config);
|
|
|
|
const char* linkType = "SHARED";
|
|
if(this->Target->GetType() == cmTarget::MODULE_LIBRARY)
|
|
{
|
|
linkType = "MODULE";
|
|
}
|
|
if(this->Target->GetType() == cmTarget::EXECUTABLE)
|
|
{
|
|
linkType = "EXE";
|
|
}
|
|
std::string stackVar = "CMAKE_";
|
|
stackVar += linkLanguage;
|
|
stackVar += "_STACK_SIZE";
|
|
const char* stackVal = this->Makefile->GetDefinition(stackVar.c_str());
|
|
std::string flags;
|
|
if(stackVal)
|
|
{
|
|
flags += " ";
|
|
flags += stackVal;
|
|
}
|
|
// assume incremental linking
|
|
std::string linkFlagVarBase = "CMAKE_";
|
|
linkFlagVarBase += linkType;
|
|
linkFlagVarBase += "_LINKER_FLAGS";
|
|
flags += " ";
|
|
flags += this->
|
|
Target->GetMakefile()->GetRequiredDefinition(linkFlagVarBase.c_str());
|
|
std::string linkFlagVar = linkFlagVarBase + "_" + CONFIG;
|
|
flags += " ";
|
|
flags += this->
|
|
Target->GetMakefile()->GetRequiredDefinition(linkFlagVar.c_str());
|
|
const char* targetLinkFlags = this->Target->GetProperty("LINK_FLAGS");
|
|
if(targetLinkFlags)
|
|
{
|
|
flags += " ";
|
|
flags += targetLinkFlags;
|
|
}
|
|
std::string flagsProp = "LINK_FLAGS_";
|
|
flagsProp += CONFIG;
|
|
if(const char* flagsConfig = this->Target->GetProperty(flagsProp.c_str()))
|
|
{
|
|
flags += " ";
|
|
flags += flagsConfig;
|
|
}
|
|
cmVisualStudioGeneratorOptions
|
|
linkOptions(this->LocalGenerator,
|
|
cmVisualStudioGeneratorOptions::Linker,
|
|
cmVS10LinkFlagTable, 0, this);
|
|
if ( this->Target->GetPropertyAsBool("WIN32_EXECUTABLE") )
|
|
{
|
|
flags += " /SUBSYSTEM:WINDOWS";
|
|
}
|
|
else
|
|
{
|
|
flags += " /SUBSYSTEM:CONSOLE";
|
|
}
|
|
cmSystemTools::ReplaceString(flags, "/INCREMENTAL:YES", "");
|
|
cmSystemTools::ReplaceString(flags, "/INCREMENTAL:NO", "");
|
|
std::string standardLibsVar = "CMAKE_";
|
|
standardLibsVar += linkLanguage;
|
|
standardLibsVar += "_STANDARD_LIBRARIES";
|
|
std::string
|
|
libs = this->Makefile->GetSafeDefinition(standardLibsVar.c_str());
|
|
// Remove trailing spaces from libs
|
|
std::string::size_type pos = libs.size()-1;
|
|
if(libs.size() != 0)
|
|
{
|
|
while(libs[pos] == ' ')
|
|
{
|
|
pos--;
|
|
}
|
|
}
|
|
if(pos != libs.size()-1)
|
|
{
|
|
libs = libs.substr(0, pos+1);
|
|
}
|
|
// Replace spaces in libs with ;
|
|
cmSystemTools::ReplaceString(libs, " ", ";");
|
|
cmComputeLinkInformation* pcli =
|
|
this->Target->GetLinkInformation(config.c_str());
|
|
if(!pcli)
|
|
{
|
|
cmSystemTools::Error
|
|
("CMake can not compute cmComputeLinkInformation for target:",
|
|
this->Name.c_str());
|
|
return;
|
|
}
|
|
// add the libraries for the target to libs string
|
|
cmComputeLinkInformation& cli = *pcli;
|
|
this->AddLibraries(cli, libs);
|
|
linkOptions.AddFlag("AdditionalDependencies", libs.c_str());
|
|
|
|
std::vector<std::string> const& ldirs = cli.GetDirectories();
|
|
const char* sep = "";
|
|
std::string linkDirs;
|
|
for(std::vector<std::string>::const_iterator d = ldirs.begin();
|
|
d != ldirs.end(); ++d)
|
|
{
|
|
// first just full path
|
|
linkDirs += sep;
|
|
linkDirs += *d;
|
|
sep = ";";
|
|
linkDirs += sep;
|
|
// next path with configuration type Debug, Release, etc
|
|
linkDirs += *d;
|
|
linkDirs += "/$(Configuration)";
|
|
linkDirs += sep;
|
|
}
|
|
linkDirs += "%(AdditionalLibraryDirectories)";
|
|
linkOptions.AddFlag("AdditionalLibraryDirectories", linkDirs.c_str());
|
|
linkOptions.AddFlag("AdditionalDependencies", libs.c_str());
|
|
linkOptions.AddFlag("Version", "");
|
|
if(linkOptions.IsDebug() || flags.find("/debug") != flags.npos)
|
|
{
|
|
linkOptions.AddFlag("GenerateDebugInformation", "true");
|
|
}
|
|
else
|
|
{
|
|
linkOptions.AddFlag("GenerateDebugInformation", "false");
|
|
}
|
|
std::string targetName;
|
|
std::string targetNameSO;
|
|
std::string targetNameFull;
|
|
std::string targetNameImport;
|
|
std::string targetNamePDB;
|
|
if(this->Target->GetType() == cmTarget::EXECUTABLE)
|
|
{
|
|
this->Target->GetExecutableNames(targetName, targetNameFull,
|
|
targetNameImport, targetNamePDB,
|
|
config.c_str());
|
|
}
|
|
else
|
|
{
|
|
this->Target->GetLibraryNames(targetName, targetNameSO, targetNameFull,
|
|
targetNameImport, targetNamePDB,
|
|
config.c_str());
|
|
}
|
|
|
|
std::string dir = this->Target->GetDirectory(config.c_str());
|
|
dir += "/";
|
|
std::string pdb = dir;
|
|
pdb += targetNamePDB;
|
|
std::string imLib = this->Target->GetDirectory(config.c_str(), true);
|
|
imLib += "/";
|
|
imLib += targetNameImport;
|
|
|
|
linkOptions.AddFlag("ImportLibrary", imLib.c_str());
|
|
linkOptions.AddFlag("ProgramDataBaseFile", pdb.c_str());
|
|
linkOptions.Parse(flags.c_str());
|
|
if(!this->GeneratorTarget->ModuleDefinitionFile.empty())
|
|
{
|
|
linkOptions.AddFlag("ModuleDefinitionFile",
|
|
this->GeneratorTarget->ModuleDefinitionFile.c_str());
|
|
}
|
|
|
|
linkOptions.RemoveFlag("GenerateManifest");
|
|
linkOptions.OutputAdditionalOptions(*this->BuildFileStream, " ", "");
|
|
linkOptions.OutputFlagMap(*this->BuildFileStream, " ");
|
|
|
|
this->WriteString("</Link>\n", 2);
|
|
if(!this->GlobalGenerator->NeedLinkLibraryDependencies(*this->Target))
|
|
{
|
|
this->WriteString("<ProjectReference>\n", 2);
|
|
this->WriteString(
|
|
" <LinkLibraryDependencies>false</LinkLibraryDependencies>\n", 2);
|
|
this->WriteString("</ProjectReference>\n", 2);
|
|
}
|
|
}
|
|
|
|
void cmVisualStudio10TargetGenerator::AddLibraries(
|
|
cmComputeLinkInformation& cli,
|
|
std::string& libstring)
|
|
{
|
|
typedef cmComputeLinkInformation::ItemVector ItemVector;
|
|
ItemVector libs = cli.GetItems();
|
|
const char* sep = ";";
|
|
for(ItemVector::const_iterator l = libs.begin(); l != libs.end(); ++l)
|
|
{
|
|
if(l->IsPath)
|
|
{
|
|
std::string path = this->LocalGenerator->
|
|
Convert(l->Value.c_str(),
|
|
cmLocalGenerator::START_OUTPUT,
|
|
cmLocalGenerator::UNCHANGED);
|
|
this->ConvertToWindowsSlash(path);
|
|
libstring += sep;
|
|
libstring += path;
|
|
}
|
|
else
|
|
{
|
|
libstring += sep;
|
|
libstring += l->Value;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void cmVisualStudio10TargetGenerator::
|
|
WriteMidlOptions(std::string const& /*config*/,
|
|
std::vector<std::string> const & includes)
|
|
{
|
|
this->WriteString("<Midl>\n", 2);
|
|
this->OutputIncludes(includes);
|
|
this->WriteString("<OutputDirectory>$(IntDir)</OutputDirectory>\n", 3);
|
|
this->WriteString("<HeaderFileName>%(Filename).h</HeaderFileName>\n", 3);
|
|
this->WriteString(
|
|
"<TypeLibraryName>%(Filename).tlb</TypeLibraryName>\n", 3);
|
|
this->WriteString(
|
|
"<InterfaceIdentifierFileName>"
|
|
"%(Filename)_i.c</InterfaceIdentifierFileName>\n", 3);
|
|
this->WriteString("<ProxyFileName>%(Filename)_p.c</ProxyFileName>\n",3);
|
|
this->WriteString("</Midl>\n", 2);
|
|
}
|
|
|
|
|
|
void cmVisualStudio10TargetGenerator::WriteItemDefinitionGroups()
|
|
{
|
|
std::vector<std::string> *configs =
|
|
static_cast<cmGlobalVisualStudio7Generator *>
|
|
(this->GlobalGenerator)->GetConfigurations();
|
|
std::vector<std::string> includes;
|
|
this->LocalGenerator->GetIncludeDirectories(includes, this->Target);
|
|
for(std::vector<std::string>::iterator i = configs->begin();
|
|
i != configs->end(); ++i)
|
|
{
|
|
this->WritePlatformConfigTag("ItemDefinitionGroup", i->c_str(), 1);
|
|
*this->BuildFileStream << "\n";
|
|
// output cl compile flags <ClCompile></ClCompile>
|
|
if(this->Target->GetType() <= cmTarget::OBJECT_LIBRARY)
|
|
{
|
|
this->WriteClOptions(*i, includes);
|
|
// output rc compile flags <ResourceCompile></ResourceCompile>
|
|
this->WriteRCOptions(*i, includes);
|
|
}
|
|
// output midl flags <Midl></Midl>
|
|
this->WriteMidlOptions(*i, includes);
|
|
// write events
|
|
this->WriteEvents(*i);
|
|
// output link flags <Link></Link>
|
|
this->WriteLinkOptions(*i);
|
|
// output lib flags <Lib></Lib>
|
|
this->WriteLibOptions(*i);
|
|
this->WriteString("</ItemDefinitionGroup>\n", 1);
|
|
}
|
|
}
|
|
|
|
void
|
|
cmVisualStudio10TargetGenerator::WriteEvents(std::string const& configName)
|
|
{
|
|
this->WriteEvent("PreLinkEvent",
|
|
this->Target->GetPreLinkCommands(), configName);
|
|
this->WriteEvent("PreBuildEvent",
|
|
this->Target->GetPreBuildCommands(), configName);
|
|
this->WriteEvent("PostBuildEvent",
|
|
this->Target->GetPostBuildCommands(), configName);
|
|
}
|
|
|
|
void cmVisualStudio10TargetGenerator::WriteEvent(
|
|
const char* name,
|
|
std::vector<cmCustomCommand> & commands,
|
|
std::string const& configName)
|
|
{
|
|
if(commands.size() == 0)
|
|
{
|
|
return;
|
|
}
|
|
this->WriteString("<", 2);
|
|
(*this->BuildFileStream ) << name << ">\n";
|
|
cmLocalVisualStudio7Generator* lg = this->LocalGenerator;
|
|
std::string script;
|
|
const char* pre = "";
|
|
std::string comment;
|
|
for(std::vector<cmCustomCommand>::iterator i = commands.begin();
|
|
i != commands.end(); ++i)
|
|
{
|
|
cmCustomCommand& command = *i;
|
|
comment += pre;
|
|
comment += lg->ConstructComment(command);
|
|
script += pre;
|
|
pre = "\n";
|
|
script +=
|
|
cmVS10EscapeXML(lg->ConstructScript(command, configName.c_str()));
|
|
}
|
|
comment = cmVS10EscapeComment(comment);
|
|
this->WriteString("<Message>",3);
|
|
(*this->BuildFileStream ) << cmVS10EscapeXML(comment) << "</Message>\n";
|
|
this->WriteString("<Command>", 3);
|
|
(*this->BuildFileStream ) << script;
|
|
(*this->BuildFileStream ) << "</Command>" << "\n";
|
|
this->WriteString("</", 2);
|
|
(*this->BuildFileStream ) << name << ">\n";
|
|
}
|
|
|
|
|
|
void cmVisualStudio10TargetGenerator::WriteProjectReferences()
|
|
{
|
|
cmGlobalGenerator::TargetDependSet const& unordered
|
|
= this->GlobalGenerator->GetTargetDirectDepends(*this->Target);
|
|
typedef cmGlobalVisualStudioGenerator::OrderedTargetDependSet
|
|
OrderedTargetDependSet;
|
|
OrderedTargetDependSet depends(unordered);
|
|
this->WriteString("<ItemGroup>\n", 1);
|
|
for( OrderedTargetDependSet::const_iterator i = depends.begin();
|
|
i != depends.end(); ++i)
|
|
{
|
|
cmTarget* dt = *i;
|
|
// skip fortran targets as they can not be processed by MSBuild
|
|
// the only reference will be in the .sln file
|
|
if(static_cast<cmGlobalVisualStudioGenerator*>(this->GlobalGenerator)
|
|
->TargetIsFortranOnly(*dt))
|
|
{
|
|
continue;
|
|
}
|
|
this->WriteString("<ProjectReference Include=\"", 2);
|
|
cmMakefile* mf = dt->GetMakefile();
|
|
std::string name = dt->GetName();
|
|
std::string path;
|
|
const char* p = dt->GetProperty("EXTERNAL_MSPROJECT");
|
|
if(p)
|
|
{
|
|
path = p;
|
|
}
|
|
else
|
|
{
|
|
path = mf->GetStartOutputDirectory();
|
|
path += "/";
|
|
path += dt->GetName();
|
|
path += ".vcxproj";
|
|
}
|
|
(*this->BuildFileStream) << path << "\">\n";
|
|
this->WriteString("<Project>", 3);
|
|
(*this->BuildFileStream)
|
|
<< this->GlobalGenerator->GetGUID(name.c_str())
|
|
<< "</Project>\n";
|
|
this->WriteString("</ProjectReference>\n", 2);
|
|
}
|
|
this->WriteString("</ItemGroup>\n", 1);
|
|
}
|