CMake/Source/cmGlobalVisualStudio10Generator.cxx
Robert Dailey 2b958a2027 cmake-gui: Add option to specify generator toolset
The -T parameter to CMake may now be specified through cmake-gui via a
new text field in the first-time configure wizard (below the generator
chooser).

The generator factories specify whether or not they support toolsets.
This information is propagated to the Qt code and used to determine if
the selected generator should also display the optional Toolset widgets.
2015-11-17 10:03:10 -05:00

638 lines
20 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 "windows.h" // this must be first to define GetCurrentDirectory
#include "cmGlobalVisualStudio10Generator.h"
#include "cmLocalVisualStudio10Generator.h"
#include "cmMakefile.h"
#include "cmSourceFile.h"
#include "cmVisualStudioSlnData.h"
#include "cmVisualStudioSlnParser.h"
#include "cmake.h"
#include "cmAlgorithms.h"
static const char vs10generatorName[] = "Visual Studio 10 2010";
// Map generator name without year to name with year.
static const char* cmVS10GenName(const std::string& name, std::string& genName)
{
if(strncmp(name.c_str(), vs10generatorName,
sizeof(vs10generatorName)-6) != 0)
{
return 0;
}
const char* p = name.c_str() + sizeof(vs10generatorName) - 6;
if(cmHasLiteralPrefix(p, " 2010"))
{
p += 5;
}
genName = std::string(vs10generatorName) + p;
return p;
}
class cmGlobalVisualStudio10Generator::Factory
: public cmGlobalGeneratorFactory
{
public:
virtual cmGlobalGenerator*
CreateGlobalGenerator(const std::string& name, cmake* cm) const
{
std::string genName;
const char* p = cmVS10GenName(name, genName);
if(!p)
{ return 0; }
if(!*p)
{
return new cmGlobalVisualStudio10Generator(cm, genName, "");
}
if(*p++ != ' ')
{ return 0; }
if(strcmp(p, "Win64") == 0)
{
return new cmGlobalVisualStudio10Generator(cm, genName, "x64");
}
if(strcmp(p, "IA64") == 0)
{
return new cmGlobalVisualStudio10Generator(cm, genName, "Itanium");
}
return 0;
}
virtual void GetDocumentation(cmDocumentationEntry& entry) const
{
entry.Name = std::string(vs10generatorName) + " [arch]";
entry.Brief =
"Generates Visual Studio 2010 project files. "
"Optional [arch] can be \"Win64\" or \"IA64\"."
;
}
virtual void GetGenerators(std::vector<std::string>& names) const
{
names.push_back(vs10generatorName);
names.push_back(vs10generatorName + std::string(" IA64"));
names.push_back(vs10generatorName + std::string(" Win64"));
}
virtual bool SupportsToolset() const { return true; }
};
//----------------------------------------------------------------------------
cmGlobalGeneratorFactory* cmGlobalVisualStudio10Generator::NewFactory()
{
return new Factory;
}
//----------------------------------------------------------------------------
cmGlobalVisualStudio10Generator::cmGlobalVisualStudio10Generator(cmake* cm,
const std::string& name, const std::string& platformName)
: cmGlobalVisualStudio8Generator(cm, name, platformName)
{
std::string vc10Express;
this->ExpressEdition = cmSystemTools::ReadRegistryValue(
"HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VCExpress\\10.0\\Setup\\VC;"
"ProductDir", vc10Express, cmSystemTools::KeyWOW64_32);
this->SystemIsWindowsCE = false;
this->SystemIsWindowsPhone = false;
this->SystemIsWindowsStore = false;
this->MSBuildCommandInitialized = false;
this->Version = VS10;
}
//----------------------------------------------------------------------------
bool
cmGlobalVisualStudio10Generator::MatchesGeneratorName(
const std::string& name) const
{
std::string genName;
if(cmVS10GenName(name, genName))
{
return genName == this->GetName();
}
return false;
}
//----------------------------------------------------------------------------
bool cmGlobalVisualStudio10Generator::SetSystemName(std::string const& s,
cmMakefile* mf)
{
this->SystemName = s;
this->SystemVersion = mf->GetSafeDefinition("CMAKE_SYSTEM_VERSION");
if(!this->InitializeSystem(mf))
{
return false;
}
return this->cmGlobalVisualStudio8Generator::SetSystemName(s, mf);
}
//----------------------------------------------------------------------------
bool
cmGlobalVisualStudio10Generator::SetGeneratorPlatform(std::string const& p,
cmMakefile* mf)
{
if(!this->cmGlobalVisualStudio8Generator::SetGeneratorPlatform(p, mf))
{
return false;
}
if(this->GetPlatformName() == "Itanium" || this->GetPlatformName() == "x64")
{
if(this->IsExpressEdition() && !this->Find64BitTools(mf))
{
return false;
}
}
return true;
}
//----------------------------------------------------------------------------
bool
cmGlobalVisualStudio10Generator::SetGeneratorToolset(std::string const& ts,
cmMakefile* mf)
{
if (this->SystemIsWindowsCE && ts.empty() &&
this->DefaultPlatformToolset.empty())
{
std::ostringstream e;
e << this->GetName() << " Windows CE version '" << this->SystemVersion
<< "' requires CMAKE_GENERATOR_TOOLSET to be set.";
mf->IssueMessage(cmake::FATAL_ERROR, e.str());
return false;
}
this->GeneratorToolset = ts;
if(const char* toolset = this->GetPlatformToolset())
{
mf->AddDefinition("CMAKE_VS_PLATFORM_TOOLSET", toolset);
}
return true;
}
//----------------------------------------------------------------------------
bool cmGlobalVisualStudio10Generator::InitializeSystem(cmMakefile* mf)
{
if (this->SystemName == "Windows")
{
if (!this->InitializeWindows(mf))
{
return false;
}
}
else if (this->SystemName == "WindowsCE")
{
this->SystemIsWindowsCE = true;
if (!this->InitializeWindowsCE(mf))
{
return false;
}
}
else if (this->SystemName == "WindowsPhone")
{
this->SystemIsWindowsPhone = true;
if(!this->InitializeWindowsPhone(mf))
{
return false;
}
}
else if (this->SystemName == "WindowsStore")
{
this->SystemIsWindowsStore = true;
if(!this->InitializeWindowsStore(mf))
{
return false;
}
}
else if(this->SystemName == "Android")
{
if(this->DefaultPlatformName != "Win32")
{
std::ostringstream e;
e << "CMAKE_SYSTEM_NAME is 'Android' but CMAKE_GENERATOR "
<< "specifies a platform too: '" << this->GetName() << "'";
mf->IssueMessage(cmake::FATAL_ERROR, e.str());
return false;
}
std::string v = this->GetInstalledNsightTegraVersion();
if(v.empty())
{
mf->IssueMessage(cmake::FATAL_ERROR,
"CMAKE_SYSTEM_NAME is 'Android' but "
"'NVIDIA Nsight Tegra Visual Studio Edition' "
"is not installed.");
return false;
}
this->DefaultPlatformName = "Tegra-Android";
this->DefaultPlatformToolset = "Default";
this->NsightTegraVersion = v;
mf->AddDefinition("CMAKE_VS_NsightTegra_VERSION", v.c_str());
}
return true;
}
//----------------------------------------------------------------------------
bool cmGlobalVisualStudio10Generator::InitializeWindows(cmMakefile*)
{
return true;
}
//----------------------------------------------------------------------------
bool cmGlobalVisualStudio10Generator::InitializeWindowsCE(cmMakefile* mf)
{
if (this->DefaultPlatformName != "Win32")
{
std::ostringstream e;
e << "CMAKE_SYSTEM_NAME is 'WindowsCE' but CMAKE_GENERATOR "
<< "specifies a platform too: '" << this->GetName() << "'";
mf->IssueMessage(cmake::FATAL_ERROR, e.str());
return false;
}
this->DefaultPlatformToolset = this->SelectWindowsCEToolset();
return true;
}
//----------------------------------------------------------------------------
bool cmGlobalVisualStudio10Generator::InitializeWindowsPhone(cmMakefile* mf)
{
std::ostringstream e;
e << this->GetName() << " does not support Windows Phone.";
mf->IssueMessage(cmake::FATAL_ERROR, e.str());
return false;
}
//----------------------------------------------------------------------------
bool cmGlobalVisualStudio10Generator::InitializeWindowsStore(cmMakefile* mf)
{
std::ostringstream e;
e << this->GetName() << " does not support Windows Store.";
mf->IssueMessage(cmake::FATAL_ERROR, e.str());
return false;
}
//----------------------------------------------------------------------------
bool
cmGlobalVisualStudio10Generator::SelectWindowsPhoneToolset(
std::string& toolset) const
{
toolset = "";
return false;
}
//----------------------------------------------------------------------------
bool
cmGlobalVisualStudio10Generator::SelectWindowsStoreToolset(
std::string& toolset) const
{
toolset = "";
return false;
}
//----------------------------------------------------------------------------
std::string cmGlobalVisualStudio10Generator::SelectWindowsCEToolset() const
{
if (this->SystemVersion == "8.0")
{
return "CE800";
}
return "";
}
//----------------------------------------------------------------------------
void cmGlobalVisualStudio10Generator::WriteSLNHeader(std::ostream& fout)
{
fout << "Microsoft Visual Studio Solution File, Format Version 11.00\n";
if (this->ExpressEdition)
{
fout << "# Visual C++ Express 2010\n";
}
else
{
fout << "# Visual Studio 2010\n";
}
}
///! Create a local generator appropriate to this Global Generator
cmLocalGenerator* cmGlobalVisualStudio10Generator::CreateLocalGenerator(
cmMakefile* mf)
{
return new cmLocalVisualStudio10Generator(this, mf);
}
void cmGlobalVisualStudio10Generator::Generate()
{
this->LongestSource = LongestSourcePath();
this->cmGlobalVisualStudio8Generator::Generate();
if(this->LongestSource.Length > 0)
{
cmLocalGenerator* lg = this->LongestSource.Target->GetLocalGenerator();
std::ostringstream e;
e <<
"The binary and/or source directory paths may be too long to generate "
"Visual Studio 10 files for this project. "
"Consider choosing shorter directory names to build this project with "
"Visual Studio 10. "
"A more detailed explanation follows."
"\n"
"There is a bug in the VS 10 IDE that renders property dialog fields "
"blank for files referenced by full path in the project file. "
"However, CMake must reference at least one file by full path:\n"
" " << this->LongestSource.SourceFile->GetFullPath() << "\n"
"This is because some Visual Studio tools would append the relative "
"path to the end of the referencing directory path, as in:\n"
" " << lg->GetCurrentBinaryDirectory() << "/"
<< this->LongestSource.SourceRel << "\n"
"and then incorrectly complain that the file does not exist because "
"the path length is too long for some internal buffer or API. "
"To avoid this problem CMake must use a full path for this file "
"which then triggers the VS 10 property dialog bug.";
lg->IssueMessage(cmake::WARNING, e.str().c_str());
}
}
//----------------------------------------------------------------------------
void cmGlobalVisualStudio10Generator
::EnableLanguage(std::vector<std::string>const & lang,
cmMakefile *mf, bool optional)
{
cmGlobalVisualStudio8Generator::EnableLanguage(lang, mf, optional);
}
//----------------------------------------------------------------------------
const char* cmGlobalVisualStudio10Generator::GetPlatformToolset() const
{
if(!this->GeneratorToolset.empty())
{
return this->GeneratorToolset.c_str();
}
if(!this->DefaultPlatformToolset.empty())
{
return this->DefaultPlatformToolset.c_str();
}
return 0;
}
//----------------------------------------------------------------------------
void cmGlobalVisualStudio10Generator::FindMakeProgram(cmMakefile* mf)
{
this->cmGlobalVisualStudio8Generator::FindMakeProgram(mf);
mf->AddDefinition("CMAKE_VS_MSBUILD_COMMAND",
this->GetMSBuildCommand().c_str());
}
//----------------------------------------------------------------------------
std::string const& cmGlobalVisualStudio10Generator::GetMSBuildCommand()
{
if(!this->MSBuildCommandInitialized)
{
this->MSBuildCommandInitialized = true;
this->MSBuildCommand = this->FindMSBuildCommand();
}
return this->MSBuildCommand;
}
//----------------------------------------------------------------------------
std::string cmGlobalVisualStudio10Generator::FindMSBuildCommand()
{
std::string msbuild;
std::string mskey =
"HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\MSBuild\\ToolsVersions\\";
mskey += this->GetToolsVersion();
mskey += ";MSBuildToolsPath";
if(cmSystemTools::ReadRegistryValue(mskey.c_str(), msbuild,
cmSystemTools::KeyWOW64_32))
{
cmSystemTools::ConvertToUnixSlashes(msbuild);
msbuild += "/";
}
msbuild += "MSBuild.exe";
return msbuild;
}
//----------------------------------------------------------------------------
std::string cmGlobalVisualStudio10Generator::FindDevEnvCommand()
{
if(this->ExpressEdition)
{
// Visual Studio Express >= 10 do not have "devenv.com" or
// "VCExpress.exe" that we can use to build reliably.
// Tell the caller it needs to use MSBuild instead.
return "";
}
// Skip over the cmGlobalVisualStudio8Generator implementation because
// we expect a real devenv and do not want to look for VCExpress.
return this->cmGlobalVisualStudio71Generator::FindDevEnvCommand();
}
//----------------------------------------------------------------------------
void cmGlobalVisualStudio10Generator::GenerateBuildCommand(
std::vector<std::string>& makeCommand,
const std::string& makeProgram,
const std::string& projectName,
const std::string& projectDir,
const std::string& targetName,
const std::string& config,
bool fast, bool verbose,
std::vector<std::string> const& makeOptions)
{
// Select the caller- or user-preferred make program, else MSBuild.
std::string makeProgramSelected =
this->SelectMakeProgram(makeProgram, this->GetMSBuildCommand());
// Check if the caller explicitly requested a devenv tool.
std::string makeProgramLower = makeProgramSelected;
cmSystemTools::LowerCase(makeProgramLower);
bool useDevEnv =
(makeProgramLower.find("devenv") != std::string::npos ||
makeProgramLower.find("vcexpress") != std::string::npos);
// MSBuild is preferred (and required for VS Express), but if the .sln has
// an Intel Fortran .vfproj then we have to use devenv. Parse it to find out.
cmSlnData slnData;
{
std::string slnFile;
if(!projectDir.empty())
{
slnFile = projectDir;
slnFile += "/";
}
slnFile += projectName;
slnFile += ".sln";
cmVisualStudioSlnParser parser;
if(parser.ParseFile(slnFile, slnData,
cmVisualStudioSlnParser::DataGroupProjects))
{
std::vector<cmSlnProjectEntry> slnProjects = slnData.GetProjects();
for(std::vector<cmSlnProjectEntry>::iterator i = slnProjects.begin();
!useDevEnv && i != slnProjects.end(); ++i)
{
std::string proj = i->GetRelativePath();
if(proj.size() > 7 &&
proj.substr(proj.size()-7) == ".vfproj")
{
useDevEnv = true;
}
}
}
}
if(useDevEnv)
{
// Use devenv to build solutions containing Intel Fortran projects.
cmGlobalVisualStudio7Generator::GenerateBuildCommand(
makeCommand, makeProgram, projectName, projectDir,
targetName, config, fast, verbose, makeOptions);
return;
}
makeCommand.push_back(makeProgramSelected);
std::string realTarget = targetName;
// msbuild.exe CxxOnly.sln /t:Build /p:Configuration=Debug /target:ALL_BUILD
if(realTarget.empty())
{
realTarget = "ALL_BUILD";
}
if ( realTarget == "clean" )
{
makeCommand.push_back(std::string(projectName)+".sln");
makeCommand.push_back("/t:Clean");
}
else
{
std::string targetProject(realTarget);
targetProject += ".vcxproj";
if (targetProject.find('/') == std::string::npos)
{
// it might be in a subdir
if (cmSlnProjectEntry const* proj =
slnData.GetProjectByName(realTarget))
{
targetProject = proj->GetRelativePath();
cmSystemTools::ConvertToUnixSlashes(targetProject);
}
}
makeCommand.push_back(targetProject);
}
std::string configArg = "/p:Configuration=";
if(!config.empty())
{
configArg += config;
}
else
{
configArg += "Debug";
}
makeCommand.push_back(configArg);
makeCommand.push_back(std::string("/p:VisualStudioVersion=")+
this->GetIDEVersion());
makeCommand.insert(makeCommand.end(),
makeOptions.begin(), makeOptions.end());
}
//----------------------------------------------------------------------------
bool cmGlobalVisualStudio10Generator::Find64BitTools(cmMakefile* mf)
{
if(this->GetPlatformToolset())
{
return true;
}
// This edition does not come with 64-bit tools. Look for them.
//
// TODO: Detect available tools? x64\v100 exists but does not work?
// HKLM\\SOFTWARE\\Microsoft\\MSBuild\\ToolsVersions\\4.0;VCTargetsPath
// c:/Program Files (x86)/MSBuild/Microsoft.Cpp/v4.0/Platforms/
// {Itanium,Win32,x64}/PlatformToolsets/{v100,v90,Windows7.1SDK}
std::string winSDK_7_1;
if(cmSystemTools::ReadRegistryValue(
"HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Microsoft SDKs\\"
"Windows\\v7.1;InstallationFolder", winSDK_7_1))
{
std::ostringstream m;
m << "Found Windows SDK v7.1: " << winSDK_7_1;
mf->DisplayStatus(m.str().c_str(), -1);
this->DefaultPlatformToolset = "Windows7.1SDK";
return true;
}
else
{
std::ostringstream e;
e << "Cannot enable 64-bit tools with Visual Studio 2010 Express.\n"
<< "Install the Microsoft Windows SDK v7.1 to get 64-bit tools:\n"
<< " http://msdn.microsoft.com/en-us/windows/bb980924.aspx";
mf->IssueMessage(cmake::FATAL_ERROR, e.str().c_str());
cmSystemTools::SetFatalErrorOccured();
return false;
}
}
//----------------------------------------------------------------------------
std::string
cmGlobalVisualStudio10Generator
::GenerateRuleFile(std::string const& output) const
{
// The VS 10 generator needs to create the .rule files on disk.
// Hide them away under the CMakeFiles directory.
std::string ruleDir = this->GetCMakeInstance()->GetHomeOutputDirectory();
ruleDir += cmake::GetCMakeFilesDirectory();
ruleDir += "/";
ruleDir += cmSystemTools::ComputeStringMD5(
cmSystemTools::GetFilenamePath(output).c_str());
std::string ruleFile = ruleDir + "/";
ruleFile += cmSystemTools::GetFilenameName(output);
ruleFile += ".rule";
return ruleFile;
}
//----------------------------------------------------------------------------
void cmGlobalVisualStudio10Generator::PathTooLong(
cmGeneratorTarget *target, cmSourceFile const* sf,
std::string const& sfRel)
{
size_t len =
(strlen(target->GetLocalGenerator()->GetCurrentBinaryDirectory()) +
1 + sfRel.length());
if(len > this->LongestSource.Length)
{
this->LongestSource.Length = len;
this->LongestSource.Target = target;
this->LongestSource.SourceFile = sf;
this->LongestSource.SourceRel = sfRel;
}
}
//----------------------------------------------------------------------------
bool cmGlobalVisualStudio10Generator::UseFolderProperty()
{
return IsExpressEdition() ? false : cmGlobalGenerator::UseFolderProperty();
}
//----------------------------------------------------------------------------
bool cmGlobalVisualStudio10Generator::IsNsightTegra() const
{
return !this->NsightTegraVersion.empty();
}
//----------------------------------------------------------------------------
std::string cmGlobalVisualStudio10Generator::GetNsightTegraVersion() const
{
return this->NsightTegraVersion;
}
//----------------------------------------------------------------------------
std::string cmGlobalVisualStudio10Generator::GetInstalledNsightTegraVersion()
{
std::string version;
cmSystemTools::ReadRegistryValue(
"HKEY_LOCAL_MACHINE\\SOFTWARE\\NVIDIA Corporation\\Nsight Tegra;"
"Version", version, cmSystemTools::KeyWOW64_32);
return version;
}