CMake/Source/cmVisualStudio10TargetGenerator.cxx
Stephen Kelly fe732264e9 Add the INTERFACE_LIBRARY target type.
This target type only contains INTERFACE_* properties, so it can be
used as a structural node. The target-specific commands enforce
that they may only be used with the INTERFACE keyword when used
with INTERFACE_LIBRARY targets. The old-style target properties
matching LINK_INTERFACE_LIBRARIES_<CONFIG> are always ignored for
this target type.

The name of the INTERFACE_LIBRARY must match a validity generator
expression. The validity is similar to that of an ALIAS target,
but with the additional restriction that it may not contain
double colons. Double colons will carry the meaning of IMPORTED
or ALIAS targets in CMake 2.8.13.

An ALIAS target may be created for an INTERFACE library.

At this point it can not be exported and does not appear in the
buildsystem and project files are not created for them. That may
be added as a feature in a later commit.

The generators need some changes to handle the INTERFACE_LIBRARY
targets returned by cmComputeLinkInterface::GetItems. The Ninja
generator does not use that API, so it doesn't require changes
related to that.
2013-10-07 19:56:31 -04:00

1886 lines
62 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 "cmVS11CLFlagTable.h"
#include "cmVS11LinkFlagTable.h"
#include "cmVS11LibFlagTable.h"
#include "cmVS12CLFlagTable.h"
#include "cmVS12LinkFlagTable.h"
#include "cmVS12LibFlagTable.h"
#include <cmsys/auto_ptr.hxx>
static cmVS7FlagTable const*
cmVSGetCLFlagTable(cmLocalVisualStudioGenerator* lg)
{
if(lg->GetVersion() >= cmLocalVisualStudioGenerator::VS12)
{ return cmVS12CLFlagTable; }
else if(lg->GetVersion() == cmLocalVisualStudioGenerator::VS11)
{ return cmVS11CLFlagTable; }
else
{ return cmVS10CLFlagTable; }
}
static cmVS7FlagTable const*
cmVSGetLibFlagTable(cmLocalVisualStudioGenerator* lg)
{
if(lg->GetVersion() >= cmLocalVisualStudioGenerator::VS12)
{ return cmVS12LibFlagTable; }
else if(lg->GetVersion() == cmLocalVisualStudioGenerator::VS11)
{ return cmVS11LibFlagTable; }
else
{ return cmVS10LibFlagTable; }
}
static cmVS7FlagTable const*
cmVSGetLinkFlagTable(cmLocalVisualStudioGenerator* lg)
{
if(lg->GetVersion() >= cmLocalVisualStudioGenerator::VS12)
{ return cmVS12LinkFlagTable; }
else if(lg->GetVersion() == cmLocalVisualStudioGenerator::VS11)
{ return cmVS11LinkFlagTable; }
else
{ return cmVS10LinkFlagTable; }
}
static std::string cmVS10EscapeXML(std::string arg)
{
cmSystemTools::ReplaceString(arg, "&", "&amp;");
cmSystemTools::ReplaceString(arg, "<", "&lt;");
cmSystemTools::ReplaceString(arg, ">", "&gt;");
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;
}
for(OptionsMap::iterator i = this->LinkOptions.begin();
i != this->LinkOptions.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;
}
if(!this->ComputeLinkOptions())
{
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);
//get the tools version to use
const std::string toolsVer(this->GlobalGenerator->GetToolsVersion());
std::string project_defaults="<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
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);
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";
}
const char* vsGlobalRootNamespace =
this->Target->GetProperty("VS_GLOBAL_ROOTNAMESPACE");
if(vsGlobalRootNamespace)
{
this->WriteString("<RootNamespace>", 2);
(*this->BuildFileStream) << cmVS10EscapeXML(vsGlobalRootNamespace) <<
"</RootNamespace>\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";
if(const char* targetFrameworkVersion = this->Target->GetProperty(
"VS_DOTNET_TARGET_FRAMEWORK_VERSION"))
{
this->WriteString("<TargetFrameworkVersion>", 2);
(*this->BuildFileStream) << targetFrameworkVersion
<< "</TargetFrameworkVersion>\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);
if (this->GlobalGenerator->IsMasmEnabled())
{
this->WriteString("<Import Project=\"$(VCTargetsPath)\\"
"BuildCustomizations\\masm.props\" />\n", 2);
}
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->WriteEmbeddedResourceGroup();
this->WriteWinRTReferences();
this->WriteProjectReferences();
this->WriteString(
"<Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.targets\""
" />\n", 1);
this->WriteString("<ImportGroup Label=\"ExtensionTargets\">\n", 1);
if (this->GlobalGenerator->IsMasmEnabled())
{
this->WriteString("<Import Project=\"$(VCTargetsPath)\\"
"BuildCustomizations\\masm.targets\" />\n", 2);
}
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::WriteEmbeddedResourceGroup()
{
std::vector<cmSourceFile*> const& resxObjs =
this->GeneratorTarget->ResxSources;
if(!resxObjs.empty())
{
this->WriteString("<ItemGroup>\n", 1);
for(std::vector<cmSourceFile*>::const_iterator oi = resxObjs.begin();
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);
}
}
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;
case cmTarget::GLOBAL_TARGET:
case cmTarget::UNKNOWN_LIBRARY:
case cmTarget::INTERFACE_LIBRARY:
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("<WindowsAppContainer>true"
"</WindowsAppContainer>\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();
// VS 10 will always rebuild a custom command attached to a .rule
// file that doesn't exist so create the file explicitly.
if (source->GetPropertyAsBool("__CMAKE_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->WriteSource("CustomBuild", source, ">\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";
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";
}
}
this->WriteString("</CustomBuild>\n", 2);
}
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,
cmLocalGenerator::UNCHANGED);
}
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;
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);
}
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;
//get the tools version to use
const std::string toolsVer(this->GlobalGenerator->GetToolsVersion());
std::string project_defaults="<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
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);
for(ToolSourceMap::const_iterator ti = this->Tools.begin();
ti != this->Tools.end(); ++ti)
{
this->WriteGroupSources(ti->first.c_str(), ti->second, sourceGroups);
}
std::vector<cmSourceFile*> const& resxObjs =
this->GeneratorTarget->ResxSources;
if(!resxObjs.empty())
{
this->WriteString("<ItemGroup>\n", 1);
for(std::vector<cmSourceFile*>::const_iterator oi = resxObjs.begin();
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);
}
// 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);
}
if(!this->GeneratorTarget->ResxSources.empty())
{
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);
}
this->WriteString("</ItemGroup>\n", 1);
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,
ToolSources const& sources,
std::vector<cmSourceGroup>& sourceGroups)
{
this->WriteString("<ItemGroup>\n", 1);
for(ToolSources::const_iterator s = sources.begin();
s != sources.end(); ++s)
{
cmSourceFile* sf = s->SourceFile;
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 = this->ConvertPath(source, s->RelativePath);
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, const char* end)
{
// 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
// conversion uses full paths outside the build tree to allow deeper trees.
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
// 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))
{
forceRelative = true;
sourceFile = sourceRel;
}
else
{
this->GlobalGenerator->PathTooLong(this->Target, sf, sourceRel);
}
}
this->ConvertToWindowsSlash(sourceFile);
this->WriteString("<", 2);
(*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");
}
ToolSource toolSource = {sf, forceRelative};
this->Tools[tool].push_back(toolSource);
}
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();
const char* tool = NULL;
if (strcmp(lang, "C") == 0 || strcmp(lang, "CXX") == 0)
{
tool = "ClCompile";
}
else if (strcmp(lang, "ASM_MASM") == 0 &&
this->GlobalGenerator->IsMasmEnabled())
{
tool = "MASM";
}
else if (strcmp(lang, "RC") == 0)
{
tool = "ResourceCompile";
}
if (tool)
{
this->WriteSource(tool, *si, " ");
if (this->OutputSourceSpecificFlags(*si))
{
this->WriteString("</", 2);
(*this->BuildFileStream ) << tool << ">\n";
}
else
{
(*this->BuildFileStream ) << " />\n";
}
}
else
{
this->WriteSource("None", *si);
}
}
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.
this->WriteSources("Object", this->GeneratorTarget->ExternalObjects);
}
else
{
// 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.
for(std::vector<cmSourceFile*>::const_iterator
si = this->GeneratorTarget->ExternalObjects.begin();
si != this->GeneratorTarget->ExternalObjects.end(); ++si)
{
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;
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,
cmVSGetCLFlagTable(this->LocalGenerator), 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)
{
// 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;
}
Options& linkOptions = *(this->LinkOptions[configName]);
const char* incremental = linkOptions.GetFlag("LinkIncremental");
this->WritePlatformConfigTag("LinkIncremental", configName.c_str(), 3);
*this->BuildFileStream << (incremental?incremental:"true")
<< "</LinkIncremental>\n";
linkOptions.RemoveFlag("LinkIncremental");
const char* manifest = linkOptions.GetFlag("GenerateManifest");
this->WritePlatformConfigTag("GenerateManifest", configName.c_str(), 3);
*this->BuildFileStream << (manifest?manifest:"true")
<< "</GenerateManifest>\n";
linkOptions.RemoveFlag("GenerateManifest");
// Some link options belong here. Use them now and remove them so that
// WriteLinkOptions does not use them.
const char* flags[] = {
"LinkDelaySign",
"LinkKeyFile",
0};
for(const char** f = flags; *f; ++f)
{
const char* flag = *f;
if(const char* value = linkOptions.GetFlag(flag))
{
this->WritePlatformConfigTag(flag, configName.c_str(), 3);
*this->BuildFileStream << value << "</" << flag << ">\n";
linkOptions.RemoveFlag(flag);
}
}
}
//----------------------------------------------------------------------------
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,
cmVSGetCLFlagTable(this->LocalGenerator)));
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 ";
}
this->LocalGenerator->AddCompileOptions(flags, this->Target,
linkLanguage, configName.c_str());
}
// Get preprocessor definitions for this directory.
std::string defineFlags = this->Target->GetMakefile()->GetDefineFlags();
clOptions.FixExceptionHandlingDefault();
clOptions.AddFlag("PrecompiledHeader", "NotUsing");
std::string asmLocation = configName + "/";
clOptions.AddFlag("AssemblerListingLocation", asmLocation.c_str());
clOptions.Parse(flags.c_str());
clOptions.Parse(defineFlags.c_str());
std::vector<std::string> targetDefines;
this->Target->GetCompileDefinitions(targetDefines, configName.c_str());
clOptions.AddDefines(targetDefines);
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("<ObjectFileName>$(IntDir)</ObjectFileName>\n", 3);
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 << cmVS10EscapeXML(*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;
}
std::string libflags;
this->LocalGenerator->GetStaticLibraryFlags(libflags,
cmSystemTools::UpperCase(config), this->Target);
if(!libflags.empty())
{
this->WriteString("<Lib>\n", 2);
cmVisualStudioGeneratorOptions
libOptions(this->LocalGenerator,
cmVisualStudioGeneratorOptions::Linker,
cmVSGetLibFlagTable(this->LocalGenerator), 0, this);
libOptions.Parse(libflags.c_str());
libOptions.OutputAdditionalOptions(*this->BuildFileStream, " ", "");
libOptions.OutputFlagMap(*this->BuildFileStream, " ");
this->WriteString("</Lib>\n", 2);
}
}
//----------------------------------------------------------------------------
bool cmVisualStudio10TargetGenerator::ComputeLinkOptions()
{
if(this->Target->GetType() == cmTarget::EXECUTABLE ||
this->Target->GetType() == cmTarget::SHARED_LIBRARY ||
this->Target->GetType() == cmTarget::MODULE_LIBRARY)
{
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->ComputeLinkOptions(*i))
{
return false;
}
}
}
return true;
}
//----------------------------------------------------------------------------
bool
cmVisualStudio10TargetGenerator::ComputeLinkOptions(std::string const& config)
{
cmsys::auto_ptr<Options> pOptions(
new Options(this->LocalGenerator, Options::Linker,
cmVSGetLinkFlagTable(this->LocalGenerator), 0, this));
Options& linkOptions = *pOptions;
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 false;
}
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 += " /STACK:";
flags += stackVal;
}
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;
}
if ( this->Target->GetPropertyAsBool("WIN32_EXECUTABLE") )
{
linkOptions.AddFlag("SubSystem", "Windows");
}
else
{
linkOptions.AddFlag("SubSystem", "Console");
}
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 false;
}
// 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 pdb = this->Target->GetPDBDirectory(config.c_str());
pdb += "/";
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());
}
this->LinkOptions[config] = pOptions.release();
return true;
}
//----------------------------------------------------------------------------
void
cmVisualStudio10TargetGenerator::WriteLinkOptions(std::string const& config)
{
if(this->Target->GetType() == cmTarget::STATIC_LIBRARY
|| this->Target->GetType() > cmTarget::MODULE_LIBRARY)
{
return;
}
Options& linkOptions = *(this->LinkOptions[config]);
this->WriteString("<Link>\n", 2);
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 if (!l->Target
|| l->Target->GetType() != cmTarget::INTERFACE_LIBRARY)
{
libstring += sep;
libstring += l->Value;
}
}
}
void cmVisualStudio10TargetGenerator::
WriteMidlOptions(std::string const& /*config*/,
std::vector<std::string> const & includes)
{
// This processes *any* of the .idl files specified in the project's file
// list (and passed as the item metadata %(Filename) expressing the rule
// input filename) into output files at the per-config *build* dir
// ($(IntDir)) each.
//
// IOW, this MIDL section is intended to provide a fully generic syntax
// content suitable for most cases (read: if you get errors, then it's quite
// probable that the error is on your side of the .idl setup).
//
// Also, note that the marked-as-generated _i.c file in the Visual Studio
// generator case needs to be referred to as $(IntDir)\foo_i.c at the
// project's file list, otherwise the compiler-side processing won't pick it
// up (for non-directory form, it ends up looking in project binary dir
// only). Perhaps there's something to be done to make this more automatic
// on the CMake side?
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();
for(std::vector<std::string>::iterator i = configs->begin();
i != configs->end(); ++i)
{
std::vector<std::string> includes;
this->LocalGenerator->GetIncludeDirectories(includes,
this->GeneratorTarget,
"C", i->c_str());
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;
if(dt->GetType() == cmTarget::INTERFACE_LIBRARY)
{
continue;
}
// 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);
}
bool cmVisualStudio10TargetGenerator::
IsResxHeader(const std::string& headerFile)
{
std::set<std::string>::iterator it =
this->GeneratorTarget->ExpectedResxHeaders.find(headerFile);
return it != this->GeneratorTarget->ExpectedResxHeaders.end();
}