cmGlobalVisualStudio10Generator.cxx 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543
  1. /*============================================================================
  2. CMake - Cross Platform Makefile Generator
  3. Copyright 2000-2009 Kitware, Inc., Insight Software Consortium
  4. Distributed under the OSI-approved BSD License (the "License");
  5. see accompanying file Copyright.txt for details.
  6. This software is distributed WITHOUT ANY WARRANTY; without even the
  7. implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  8. See the License for more information.
  9. ============================================================================*/
  10. #include "windows.h" // this must be first to define GetCurrentDirectory
  11. #include "cmGlobalVisualStudio10Generator.h"
  12. #include "cmAlgorithms.h"
  13. #include "cmLocalVisualStudio10Generator.h"
  14. #include "cmMakefile.h"
  15. #include "cmSourceFile.h"
  16. #include "cmVisualStudioSlnData.h"
  17. #include "cmVisualStudioSlnParser.h"
  18. #include "cmake.h"
  19. static const char vs10generatorName[] = "Visual Studio 10 2010";
  20. // Map generator name without year to name with year.
  21. static const char* cmVS10GenName(const std::string& name, std::string& genName)
  22. {
  23. if (strncmp(name.c_str(), vs10generatorName,
  24. sizeof(vs10generatorName) - 6) != 0) {
  25. return 0;
  26. }
  27. const char* p = name.c_str() + sizeof(vs10generatorName) - 6;
  28. if (cmHasLiteralPrefix(p, " 2010")) {
  29. p += 5;
  30. }
  31. genName = std::string(vs10generatorName) + p;
  32. return p;
  33. }
  34. class cmGlobalVisualStudio10Generator::Factory
  35. : public cmGlobalGeneratorFactory
  36. {
  37. public:
  38. cmGlobalGenerator* CreateGlobalGenerator(const std::string& name,
  39. cmake* cm) const CM_OVERRIDE
  40. {
  41. std::string genName;
  42. const char* p = cmVS10GenName(name, genName);
  43. if (!p) {
  44. return 0;
  45. }
  46. if (!*p) {
  47. return new cmGlobalVisualStudio10Generator(cm, genName, "");
  48. }
  49. if (*p++ != ' ') {
  50. return 0;
  51. }
  52. if (strcmp(p, "Win64") == 0) {
  53. return new cmGlobalVisualStudio10Generator(cm, genName, "x64");
  54. }
  55. if (strcmp(p, "IA64") == 0) {
  56. return new cmGlobalVisualStudio10Generator(cm, genName, "Itanium");
  57. }
  58. return 0;
  59. }
  60. void GetDocumentation(cmDocumentationEntry& entry) const CM_OVERRIDE
  61. {
  62. entry.Name = std::string(vs10generatorName) + " [arch]";
  63. entry.Brief = "Generates Visual Studio 2010 project files. "
  64. "Optional [arch] can be \"Win64\" or \"IA64\".";
  65. }
  66. void GetGenerators(std::vector<std::string>& names) const CM_OVERRIDE
  67. {
  68. names.push_back(vs10generatorName);
  69. names.push_back(vs10generatorName + std::string(" IA64"));
  70. names.push_back(vs10generatorName + std::string(" Win64"));
  71. }
  72. bool SupportsToolset() const CM_OVERRIDE { return true; }
  73. };
  74. cmGlobalGeneratorFactory* cmGlobalVisualStudio10Generator::NewFactory()
  75. {
  76. return new Factory;
  77. }
  78. cmGlobalVisualStudio10Generator::cmGlobalVisualStudio10Generator(
  79. cmake* cm, const std::string& name, const std::string& platformName)
  80. : cmGlobalVisualStudio8Generator(cm, name, platformName)
  81. {
  82. std::string vc10Express;
  83. this->ExpressEdition = cmSystemTools::ReadRegistryValue(
  84. "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VCExpress\\10.0\\Setup\\VC;"
  85. "ProductDir",
  86. vc10Express, cmSystemTools::KeyWOW64_32);
  87. this->SystemIsWindowsCE = false;
  88. this->SystemIsWindowsPhone = false;
  89. this->SystemIsWindowsStore = false;
  90. this->MSBuildCommandInitialized = false;
  91. this->Version = VS10;
  92. }
  93. bool cmGlobalVisualStudio10Generator::MatchesGeneratorName(
  94. const std::string& name) const
  95. {
  96. std::string genName;
  97. if (cmVS10GenName(name, genName)) {
  98. return genName == this->GetName();
  99. }
  100. return false;
  101. }
  102. bool cmGlobalVisualStudio10Generator::SetSystemName(std::string const& s,
  103. cmMakefile* mf)
  104. {
  105. this->SystemName = s;
  106. this->SystemVersion = mf->GetSafeDefinition("CMAKE_SYSTEM_VERSION");
  107. if (!this->InitializeSystem(mf)) {
  108. return false;
  109. }
  110. return this->cmGlobalVisualStudio8Generator::SetSystemName(s, mf);
  111. }
  112. bool cmGlobalVisualStudio10Generator::SetGeneratorPlatform(
  113. std::string const& p, cmMakefile* mf)
  114. {
  115. if (!this->cmGlobalVisualStudio8Generator::SetGeneratorPlatform(p, mf)) {
  116. return false;
  117. }
  118. if (this->GetPlatformName() == "Itanium" ||
  119. this->GetPlatformName() == "x64") {
  120. if (this->IsExpressEdition() && !this->Find64BitTools(mf)) {
  121. return false;
  122. }
  123. }
  124. return true;
  125. }
  126. bool cmGlobalVisualStudio10Generator::SetGeneratorToolset(
  127. std::string const& ts, cmMakefile* mf)
  128. {
  129. if (this->SystemIsWindowsCE && ts.empty() &&
  130. this->DefaultPlatformToolset.empty()) {
  131. std::ostringstream e;
  132. e << this->GetName() << " Windows CE version '" << this->SystemVersion
  133. << "' requires CMAKE_GENERATOR_TOOLSET to be set.";
  134. mf->IssueMessage(cmake::FATAL_ERROR, e.str());
  135. return false;
  136. }
  137. this->GeneratorToolset = ts;
  138. if (const char* toolset = this->GetPlatformToolset()) {
  139. mf->AddDefinition("CMAKE_VS_PLATFORM_TOOLSET", toolset);
  140. }
  141. return true;
  142. }
  143. bool cmGlobalVisualStudio10Generator::InitializeSystem(cmMakefile* mf)
  144. {
  145. if (this->SystemName == "Windows") {
  146. if (!this->InitializeWindows(mf)) {
  147. return false;
  148. }
  149. } else if (this->SystemName == "WindowsCE") {
  150. this->SystemIsWindowsCE = true;
  151. if (!this->InitializeWindowsCE(mf)) {
  152. return false;
  153. }
  154. } else if (this->SystemName == "WindowsPhone") {
  155. this->SystemIsWindowsPhone = true;
  156. if (!this->InitializeWindowsPhone(mf)) {
  157. return false;
  158. }
  159. } else if (this->SystemName == "WindowsStore") {
  160. this->SystemIsWindowsStore = true;
  161. if (!this->InitializeWindowsStore(mf)) {
  162. return false;
  163. }
  164. } else if (this->SystemName == "Android") {
  165. if (this->DefaultPlatformName != "Win32") {
  166. std::ostringstream e;
  167. e << "CMAKE_SYSTEM_NAME is 'Android' but CMAKE_GENERATOR "
  168. << "specifies a platform too: '" << this->GetName() << "'";
  169. mf->IssueMessage(cmake::FATAL_ERROR, e.str());
  170. return false;
  171. }
  172. std::string v = this->GetInstalledNsightTegraVersion();
  173. if (v.empty()) {
  174. mf->IssueMessage(cmake::FATAL_ERROR,
  175. "CMAKE_SYSTEM_NAME is 'Android' but "
  176. "'NVIDIA Nsight Tegra Visual Studio Edition' "
  177. "is not installed.");
  178. return false;
  179. }
  180. this->DefaultPlatformName = "Tegra-Android";
  181. this->DefaultPlatformToolset = "Default";
  182. this->NsightTegraVersion = v;
  183. mf->AddDefinition("CMAKE_VS_NsightTegra_VERSION", v.c_str());
  184. }
  185. return true;
  186. }
  187. bool cmGlobalVisualStudio10Generator::InitializeWindows(cmMakefile*)
  188. {
  189. return true;
  190. }
  191. bool cmGlobalVisualStudio10Generator::InitializeWindowsCE(cmMakefile* mf)
  192. {
  193. if (this->DefaultPlatformName != "Win32") {
  194. std::ostringstream e;
  195. e << "CMAKE_SYSTEM_NAME is 'WindowsCE' but CMAKE_GENERATOR "
  196. << "specifies a platform too: '" << this->GetName() << "'";
  197. mf->IssueMessage(cmake::FATAL_ERROR, e.str());
  198. return false;
  199. }
  200. this->DefaultPlatformToolset = this->SelectWindowsCEToolset();
  201. return true;
  202. }
  203. bool cmGlobalVisualStudio10Generator::InitializeWindowsPhone(cmMakefile* mf)
  204. {
  205. std::ostringstream e;
  206. e << this->GetName() << " does not support Windows Phone.";
  207. mf->IssueMessage(cmake::FATAL_ERROR, e.str());
  208. return false;
  209. }
  210. bool cmGlobalVisualStudio10Generator::InitializeWindowsStore(cmMakefile* mf)
  211. {
  212. std::ostringstream e;
  213. e << this->GetName() << " does not support Windows Store.";
  214. mf->IssueMessage(cmake::FATAL_ERROR, e.str());
  215. return false;
  216. }
  217. bool cmGlobalVisualStudio10Generator::SelectWindowsPhoneToolset(
  218. std::string& toolset) const
  219. {
  220. toolset = "";
  221. return false;
  222. }
  223. bool cmGlobalVisualStudio10Generator::SelectWindowsStoreToolset(
  224. std::string& toolset) const
  225. {
  226. toolset = "";
  227. return false;
  228. }
  229. std::string cmGlobalVisualStudio10Generator::SelectWindowsCEToolset() const
  230. {
  231. if (this->SystemVersion == "8.0") {
  232. return "CE800";
  233. }
  234. return "";
  235. }
  236. void cmGlobalVisualStudio10Generator::WriteSLNHeader(std::ostream& fout)
  237. {
  238. fout << "Microsoft Visual Studio Solution File, Format Version 11.00\n";
  239. if (this->ExpressEdition) {
  240. fout << "# Visual C++ Express 2010\n";
  241. } else {
  242. fout << "# Visual Studio 2010\n";
  243. }
  244. }
  245. ///! Create a local generator appropriate to this Global Generator
  246. cmLocalGenerator* cmGlobalVisualStudio10Generator::CreateLocalGenerator(
  247. cmMakefile* mf)
  248. {
  249. return new cmLocalVisualStudio10Generator(this, mf);
  250. }
  251. void cmGlobalVisualStudio10Generator::Generate()
  252. {
  253. this->LongestSource = LongestSourcePath();
  254. this->cmGlobalVisualStudio8Generator::Generate();
  255. if (this->LongestSource.Length > 0) {
  256. cmLocalGenerator* lg = this->LongestSource.Target->GetLocalGenerator();
  257. std::ostringstream e;
  258. /* clang-format off */
  259. e <<
  260. "The binary and/or source directory paths may be too long to generate "
  261. "Visual Studio 10 files for this project. "
  262. "Consider choosing shorter directory names to build this project with "
  263. "Visual Studio 10. "
  264. "A more detailed explanation follows."
  265. "\n"
  266. "There is a bug in the VS 10 IDE that renders property dialog fields "
  267. "blank for files referenced by full path in the project file. "
  268. "However, CMake must reference at least one file by full path:\n"
  269. " " << this->LongestSource.SourceFile->GetFullPath() << "\n"
  270. "This is because some Visual Studio tools would append the relative "
  271. "path to the end of the referencing directory path, as in:\n"
  272. " " << lg->GetCurrentBinaryDirectory() << "/"
  273. << this->LongestSource.SourceRel << "\n"
  274. "and then incorrectly complain that the file does not exist because "
  275. "the path length is too long for some internal buffer or API. "
  276. "To avoid this problem CMake must use a full path for this file "
  277. "which then triggers the VS 10 property dialog bug.";
  278. /* clang-format on */
  279. lg->IssueMessage(cmake::WARNING, e.str().c_str());
  280. }
  281. }
  282. void cmGlobalVisualStudio10Generator::EnableLanguage(
  283. std::vector<std::string> const& lang, cmMakefile* mf, bool optional)
  284. {
  285. cmGlobalVisualStudio8Generator::EnableLanguage(lang, mf, optional);
  286. }
  287. const char* cmGlobalVisualStudio10Generator::GetPlatformToolset() const
  288. {
  289. if (!this->GeneratorToolset.empty()) {
  290. return this->GeneratorToolset.c_str();
  291. }
  292. if (!this->DefaultPlatformToolset.empty()) {
  293. return this->DefaultPlatformToolset.c_str();
  294. }
  295. return 0;
  296. }
  297. void cmGlobalVisualStudio10Generator::FindMakeProgram(cmMakefile* mf)
  298. {
  299. this->cmGlobalVisualStudio8Generator::FindMakeProgram(mf);
  300. mf->AddDefinition("CMAKE_VS_MSBUILD_COMMAND",
  301. this->GetMSBuildCommand().c_str());
  302. }
  303. std::string const& cmGlobalVisualStudio10Generator::GetMSBuildCommand()
  304. {
  305. if (!this->MSBuildCommandInitialized) {
  306. this->MSBuildCommandInitialized = true;
  307. this->MSBuildCommand = this->FindMSBuildCommand();
  308. }
  309. return this->MSBuildCommand;
  310. }
  311. std::string cmGlobalVisualStudio10Generator::FindMSBuildCommand()
  312. {
  313. std::string msbuild;
  314. std::string mskey =
  315. "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\MSBuild\\ToolsVersions\\";
  316. mskey += this->GetToolsVersion();
  317. mskey += ";MSBuildToolsPath";
  318. if (cmSystemTools::ReadRegistryValue(mskey.c_str(), msbuild,
  319. cmSystemTools::KeyWOW64_32)) {
  320. cmSystemTools::ConvertToUnixSlashes(msbuild);
  321. msbuild += "/";
  322. }
  323. msbuild += "MSBuild.exe";
  324. return msbuild;
  325. }
  326. std::string cmGlobalVisualStudio10Generator::FindDevEnvCommand()
  327. {
  328. if (this->ExpressEdition) {
  329. // Visual Studio Express >= 10 do not have "devenv.com" or
  330. // "VCExpress.exe" that we can use to build reliably.
  331. // Tell the caller it needs to use MSBuild instead.
  332. return "";
  333. }
  334. // Skip over the cmGlobalVisualStudio8Generator implementation because
  335. // we expect a real devenv and do not want to look for VCExpress.
  336. return this->cmGlobalVisualStudio71Generator::FindDevEnvCommand();
  337. }
  338. void cmGlobalVisualStudio10Generator::GenerateBuildCommand(
  339. std::vector<std::string>& makeCommand, const std::string& makeProgram,
  340. const std::string& projectName, const std::string& projectDir,
  341. const std::string& targetName, const std::string& config, bool fast,
  342. bool verbose, std::vector<std::string> const& makeOptions)
  343. {
  344. // Select the caller- or user-preferred make program, else MSBuild.
  345. std::string makeProgramSelected =
  346. this->SelectMakeProgram(makeProgram, this->GetMSBuildCommand());
  347. // Check if the caller explicitly requested a devenv tool.
  348. std::string makeProgramLower = makeProgramSelected;
  349. cmSystemTools::LowerCase(makeProgramLower);
  350. bool useDevEnv = (makeProgramLower.find("devenv") != std::string::npos ||
  351. makeProgramLower.find("vcexpress") != std::string::npos);
  352. // MSBuild is preferred (and required for VS Express), but if the .sln has
  353. // an Intel Fortran .vfproj then we have to use devenv. Parse it to find out.
  354. cmSlnData slnData;
  355. {
  356. std::string slnFile;
  357. if (!projectDir.empty()) {
  358. slnFile = projectDir;
  359. slnFile += "/";
  360. }
  361. slnFile += projectName;
  362. slnFile += ".sln";
  363. cmVisualStudioSlnParser parser;
  364. if (parser.ParseFile(slnFile, slnData,
  365. cmVisualStudioSlnParser::DataGroupProjects)) {
  366. std::vector<cmSlnProjectEntry> slnProjects = slnData.GetProjects();
  367. for (std::vector<cmSlnProjectEntry>::iterator i = slnProjects.begin();
  368. !useDevEnv && i != slnProjects.end(); ++i) {
  369. std::string proj = i->GetRelativePath();
  370. if (proj.size() > 7 && proj.substr(proj.size() - 7) == ".vfproj") {
  371. useDevEnv = true;
  372. }
  373. }
  374. }
  375. }
  376. if (useDevEnv) {
  377. // Use devenv to build solutions containing Intel Fortran projects.
  378. cmGlobalVisualStudio7Generator::GenerateBuildCommand(
  379. makeCommand, makeProgram, projectName, projectDir, targetName, config,
  380. fast, verbose, makeOptions);
  381. return;
  382. }
  383. makeCommand.push_back(makeProgramSelected);
  384. std::string realTarget = targetName;
  385. // msbuild.exe CxxOnly.sln /t:Build /p:Configuration=Debug /target:ALL_BUILD
  386. if (realTarget.empty()) {
  387. realTarget = "ALL_BUILD";
  388. }
  389. if (realTarget == "clean") {
  390. makeCommand.push_back(std::string(projectName) + ".sln");
  391. makeCommand.push_back("/t:Clean");
  392. } else {
  393. std::string targetProject(realTarget);
  394. targetProject += ".vcxproj";
  395. if (targetProject.find('/') == std::string::npos) {
  396. // it might be in a subdir
  397. if (cmSlnProjectEntry const* proj =
  398. slnData.GetProjectByName(realTarget)) {
  399. targetProject = proj->GetRelativePath();
  400. cmSystemTools::ConvertToUnixSlashes(targetProject);
  401. }
  402. }
  403. makeCommand.push_back(targetProject);
  404. }
  405. std::string configArg = "/p:Configuration=";
  406. if (!config.empty()) {
  407. configArg += config;
  408. } else {
  409. configArg += "Debug";
  410. }
  411. makeCommand.push_back(configArg);
  412. makeCommand.push_back(std::string("/p:VisualStudioVersion=") +
  413. this->GetIDEVersion());
  414. makeCommand.insert(makeCommand.end(), makeOptions.begin(),
  415. makeOptions.end());
  416. }
  417. bool cmGlobalVisualStudio10Generator::Find64BitTools(cmMakefile* mf)
  418. {
  419. if (this->GetPlatformToolset()) {
  420. return true;
  421. }
  422. // This edition does not come with 64-bit tools. Look for them.
  423. //
  424. // TODO: Detect available tools? x64\v100 exists but does not work?
  425. // HKLM\\SOFTWARE\\Microsoft\\MSBuild\\ToolsVersions\\4.0;VCTargetsPath
  426. // c:/Program Files (x86)/MSBuild/Microsoft.Cpp/v4.0/Platforms/
  427. // {Itanium,Win32,x64}/PlatformToolsets/{v100,v90,Windows7.1SDK}
  428. std::string winSDK_7_1;
  429. if (cmSystemTools::ReadRegistryValue(
  430. "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Microsoft SDKs\\"
  431. "Windows\\v7.1;InstallationFolder",
  432. winSDK_7_1)) {
  433. std::ostringstream m;
  434. m << "Found Windows SDK v7.1: " << winSDK_7_1;
  435. mf->DisplayStatus(m.str().c_str(), -1);
  436. this->DefaultPlatformToolset = "Windows7.1SDK";
  437. return true;
  438. } else {
  439. std::ostringstream e;
  440. /* clang-format off */
  441. e << "Cannot enable 64-bit tools with Visual Studio 2010 Express.\n"
  442. << "Install the Microsoft Windows SDK v7.1 to get 64-bit tools:\n"
  443. << " http://msdn.microsoft.com/en-us/windows/bb980924.aspx";
  444. /* clang-format on */
  445. mf->IssueMessage(cmake::FATAL_ERROR, e.str().c_str());
  446. cmSystemTools::SetFatalErrorOccured();
  447. return false;
  448. }
  449. }
  450. std::string cmGlobalVisualStudio10Generator::GenerateRuleFile(
  451. std::string const& output) const
  452. {
  453. // The VS 10 generator needs to create the .rule files on disk.
  454. // Hide them away under the CMakeFiles directory.
  455. std::string ruleDir = this->GetCMakeInstance()->GetHomeOutputDirectory();
  456. ruleDir += cmake::GetCMakeFilesDirectory();
  457. ruleDir += "/";
  458. ruleDir += cmSystemTools::ComputeStringMD5(
  459. cmSystemTools::GetFilenamePath(output).c_str());
  460. std::string ruleFile = ruleDir + "/";
  461. ruleFile += cmSystemTools::GetFilenameName(output);
  462. ruleFile += ".rule";
  463. return ruleFile;
  464. }
  465. void cmGlobalVisualStudio10Generator::PathTooLong(cmGeneratorTarget* target,
  466. cmSourceFile const* sf,
  467. std::string const& sfRel)
  468. {
  469. size_t len =
  470. (strlen(target->GetLocalGenerator()->GetCurrentBinaryDirectory()) + 1 +
  471. sfRel.length());
  472. if (len > this->LongestSource.Length) {
  473. this->LongestSource.Length = len;
  474. this->LongestSource.Target = target;
  475. this->LongestSource.SourceFile = sf;
  476. this->LongestSource.SourceRel = sfRel;
  477. }
  478. }
  479. bool cmGlobalVisualStudio10Generator::IsNsightTegra() const
  480. {
  481. return !this->NsightTegraVersion.empty();
  482. }
  483. std::string cmGlobalVisualStudio10Generator::GetNsightTegraVersion() const
  484. {
  485. return this->NsightTegraVersion;
  486. }
  487. std::string cmGlobalVisualStudio10Generator::GetInstalledNsightTegraVersion()
  488. {
  489. std::string version;
  490. cmSystemTools::ReadRegistryValue(
  491. "HKEY_LOCAL_MACHINE\\SOFTWARE\\NVIDIA Corporation\\Nsight Tegra;"
  492. "Version",
  493. version, cmSystemTools::KeyWOW64_32);
  494. return version;
  495. }