/*============================================================================ 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 "cmAlgorithms.h" #include "cmLocalVisualStudio10Generator.h" #include "cmMakefile.h" #include "cmSourceFile.h" #include "cmVisualStudioSlnData.h" #include "cmVisualStudioSlnParser.h" #include "cmake.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& 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; /* clang-format off */ 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."; /* clang-format on */ lg->IssueMessage(cmake::WARNING, e.str().c_str()); } } //---------------------------------------------------------------------------- void cmGlobalVisualStudio10Generator ::EnableLanguage(std::vectorconst & 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& 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 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 slnProjects = slnData.GetProjects(); for(std::vector::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; /* clang-format off */ 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"; /* clang-format on */ 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::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; }