packaging: Initial packaging
[platform/upstream/cmake.git] / Source / cmGlobalVisualStudio10Generator.cxx
1 /*============================================================================
2   CMake - Cross Platform Makefile Generator
3   Copyright 2000-2009 Kitware, Inc., Insight Software Consortium
4
5   Distributed under the OSI-approved BSD License (the "License");
6   see accompanying file Copyright.txt for details.
7
8   This software is distributed WITHOUT ANY WARRANTY; without even the
9   implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10   See the License for more information.
11 ============================================================================*/
12 #include "windows.h" // this must be first to define GetCurrentDirectory
13 #include "cmGlobalVisualStudio10Generator.h"
14 #include "cmLocalVisualStudio10Generator.h"
15 #include "cmMakefile.h"
16 #include "cmSourceFile.h"
17 #include "cmVisualStudioSlnData.h"
18 #include "cmVisualStudioSlnParser.h"
19 #include "cmake.h"
20
21 static const char vs10Win32generatorName[] = "Visual Studio 10";
22 static const char vs10Win64generatorName[] = "Visual Studio 10 Win64";
23 static const char vs10IA64generatorName[] = "Visual Studio 10 IA64";
24
25 class cmGlobalVisualStudio10Generator::Factory
26   : public cmGlobalGeneratorFactory
27 {
28 public:
29   virtual cmGlobalGenerator* CreateGlobalGenerator(const char* name) const {
30     if(!strcmp(name, vs10Win32generatorName))
31       {
32       return new cmGlobalVisualStudio10Generator(
33         name, NULL, NULL);
34       }
35     if(!strcmp(name, vs10Win64generatorName))
36       {
37       return new cmGlobalVisualStudio10Generator(
38         name, "x64", "CMAKE_FORCE_WIN64");
39       }
40     if(!strcmp(name, vs10IA64generatorName))
41       {
42       return new cmGlobalVisualStudio10Generator(
43         name, "Itanium", "CMAKE_FORCE_IA64");
44       }
45     return 0;
46   }
47
48   virtual void GetDocumentation(cmDocumentationEntry& entry) const {
49     entry.Name = "Visual Studio 10";
50     entry.Brief = "Generates Visual Studio 10 (2010) project files.";
51     entry.Full =
52       "It is possible to append a space followed by the platform name "
53       "to create project files for a specific target platform. E.g. "
54       "\"Visual Studio 10 Win64\" will create project files for "
55       "the x64 processor; \"Visual Studio 10 IA64\" for Itanium.";
56   }
57
58   virtual void GetGenerators(std::vector<std::string>& names) const {
59     names.push_back(vs10Win32generatorName);
60     names.push_back(vs10Win64generatorName);
61     names.push_back(vs10IA64generatorName); }
62 };
63
64 //----------------------------------------------------------------------------
65 cmGlobalGeneratorFactory* cmGlobalVisualStudio10Generator::NewFactory()
66 {
67   return new Factory;
68 }
69
70 //----------------------------------------------------------------------------
71 cmGlobalVisualStudio10Generator::cmGlobalVisualStudio10Generator(
72   const char* name, const char* platformName,
73   const char* additionalPlatformDefinition)
74   : cmGlobalVisualStudio8Generator(name, platformName,
75                                    additionalPlatformDefinition)
76 {
77   this->FindMakeProgramFile = "CMakeVS10FindMake.cmake";
78   std::string vc10Express;
79   this->ExpressEdition = cmSystemTools::ReadRegistryValue(
80     "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VCExpress\\10.0\\Setup\\VC;"
81     "ProductDir", vc10Express, cmSystemTools::KeyWOW64_32);
82   this->MasmEnabled = false;
83 }
84
85 //----------------------------------------------------------------------------
86 bool
87 cmGlobalVisualStudio10Generator::SetGeneratorToolset(std::string const& ts)
88 {
89   this->PlatformToolset = ts;
90   return true;
91 }
92
93 //----------------------------------------------------------------------------
94 void cmGlobalVisualStudio10Generator::AddPlatformDefinitions(cmMakefile* mf)
95 {
96   cmGlobalVisualStudio8Generator::AddPlatformDefinitions(mf);
97   if(!this->PlatformToolset.empty())
98     {
99     mf->AddDefinition("CMAKE_VS_PLATFORM_TOOLSET",
100                       this->PlatformToolset.c_str());
101     }
102 }
103
104 //----------------------------------------------------------------------------
105 void cmGlobalVisualStudio10Generator::WriteSLNHeader(std::ostream& fout)
106 {
107   fout << "Microsoft Visual Studio Solution File, Format Version 11.00\n";
108   if (this->ExpressEdition)
109     {
110     fout << "# Visual C++ Express 2010\n";
111     }
112   else
113     {
114     fout << "# Visual Studio 2010\n";
115     }
116 }
117
118 ///! Create a local generator appropriate to this Global Generator
119 cmLocalGenerator *cmGlobalVisualStudio10Generator::CreateLocalGenerator()
120 {
121   cmLocalVisualStudio10Generator* lg =
122     new cmLocalVisualStudio10Generator(cmLocalVisualStudioGenerator::VS10);
123   lg->SetPlatformName(this->GetPlatformName());
124   lg->SetGlobalGenerator(this);
125   return lg;
126 }
127
128 //----------------------------------------------------------------------------
129 void cmGlobalVisualStudio10Generator::Generate()
130 {
131   this->LongestSource = LongestSourcePath();
132   this->cmGlobalVisualStudio8Generator::Generate();
133   if(this->LongestSource.Length > 0)
134     {
135     cmMakefile* mf = this->LongestSource.Target->GetMakefile();
136     cmOStringStream e;
137     e <<
138       "The binary and/or source directory paths may be too long to generate "
139       "Visual Studio 10 files for this project.  "
140       "Consider choosing shorter directory names to build this project with "
141       "Visual Studio 10.  "
142       "A more detailed explanation follows."
143       "\n"
144       "There is a bug in the VS 10 IDE that renders property dialog fields "
145       "blank for files referenced by full path in the project file.  "
146       "However, CMake must reference at least one file by full path:\n"
147       "  " << this->LongestSource.SourceFile->GetFullPath() << "\n"
148       "This is because some Visual Studio tools would append the relative "
149       "path to the end of the referencing directory path, as in:\n"
150       "  " << mf->GetCurrentOutputDirectory() << "/"
151       << this->LongestSource.SourceRel << "\n"
152       "and then incorrectly complain that the file does not exist because "
153       "the path length is too long for some internal buffer or API.  "
154       "To avoid this problem CMake must use a full path for this file "
155       "which then triggers the VS 10 property dialog bug.";
156     mf->IssueMessage(cmake::WARNING, e.str().c_str());
157     }
158 }
159
160 //----------------------------------------------------------------------------
161 void cmGlobalVisualStudio10Generator
162 ::EnableLanguage(std::vector<std::string>const &  lang,
163                  cmMakefile *mf, bool optional)
164 {
165   if(this->PlatformName == "Itanium" || this->PlatformName == "x64")
166     {
167     if(this->IsExpressEdition() && !this->Find64BitTools(mf))
168       {
169       return;
170       }
171     }
172
173   for(std::vector<std::string>::const_iterator it = lang.begin();
174       it != lang.end(); ++it)
175     {
176     if(*it == "ASM_MASM")
177       {
178       this->MasmEnabled = true;
179       }
180     }
181
182   cmGlobalVisualStudio8Generator::EnableLanguage(lang, mf, optional);
183 }
184
185 //----------------------------------------------------------------------------
186 const char* cmGlobalVisualStudio10Generator::GetPlatformToolset()
187 {
188   if(!this->PlatformToolset.empty())
189     {
190     return this->PlatformToolset.c_str();
191     }
192   return 0;
193 }
194
195 //----------------------------------------------------------------------------
196 std::string cmGlobalVisualStudio10Generator::GetUserMacrosDirectory()
197 {
198   std::string base;
199   std::string path;
200
201   // base begins with the VisualStudioProjectsLocation reg value...
202   if (cmSystemTools::ReadRegistryValue(
203     "HKEY_CURRENT_USER\\Software\\Microsoft\\VisualStudio\\10.0;"
204     "VisualStudioProjectsLocation",
205     base))
206     {
207     cmSystemTools::ConvertToUnixSlashes(base);
208
209     // 9.0 macros folder:
210     path = base + "/VSMacros80";
211       // *NOT* a typo; right now in Visual Studio 2008 beta the macros
212       // folder is VSMacros80... They may change it to 90 before final
213       // release of 2008 or they may not... we'll have to keep our eyes
214       // on it
215     }
216
217   // path is (correctly) still empty if we did not read the base value from
218   // the Registry value
219   return path;
220 }
221
222 //----------------------------------------------------------------------------
223 std::string cmGlobalVisualStudio10Generator::GetUserMacrosRegKeyBase()
224 {
225   return "Software\\Microsoft\\VisualStudio\\10.0\\vsmacros";
226 }
227
228
229 std::string cmGlobalVisualStudio10Generator
230 ::GenerateBuildCommand(const char* makeProgram,
231                        const char *projectName, const char *projectDir,
232                        const char* additionalOptions, const char *targetName,
233                        const char* config, bool ignoreErrors, bool fast)
234 {
235   // now build the test
236   std::string makeCommand
237     = cmSystemTools::ConvertToOutputPath(makeProgram);
238   std::string lowerCaseCommand = makeCommand;
239   cmSystemTools::LowerCase(lowerCaseCommand);
240
241   // If makeProgram is devenv, parent class knows how to generate command:
242   if (lowerCaseCommand.find("devenv") != std::string::npos ||
243       lowerCaseCommand.find("VCExpress") != std::string::npos)
244     {
245     return cmGlobalVisualStudio7Generator::GenerateBuildCommand(makeProgram,
246       projectName, projectDir, additionalOptions, targetName, config,
247       ignoreErrors, fast);
248     }
249
250   // Otherwise, assume MSBuild command line, and construct accordingly.
251
252   // if there are spaces in the makeCommand, assume a full path
253   // and convert it to a path with no spaces in it as the
254   // RunSingleCommand does not like spaces
255   if(makeCommand.find(' ') != std::string::npos)
256     {
257     cmSystemTools::GetShortPath(makeCommand.c_str(), makeCommand);
258     }
259   // msbuild.exe CxxOnly.sln /t:Build /p:Configuration=Debug /target:ALL_BUILD
260   if(!targetName || strlen(targetName) == 0)
261     {
262     targetName = "ALL_BUILD";
263     }
264   bool clean = false;
265   if ( targetName && strcmp(targetName, "clean") == 0 )
266     {
267     clean = true;
268     makeCommand += " ";
269     makeCommand += projectName;
270     makeCommand += ".sln ";
271     makeCommand += "/t:Clean ";
272     }
273   else
274     {
275     std::string targetProject(targetName);
276     targetProject += ".vcxproj";
277     if (targetProject.find('/') == std::string::npos)
278       {
279       // it might be in a subdir
280       cmVisualStudioSlnParser parser;
281       cmSlnData slnData;
282       std::string slnFile;
283       if (projectDir && *projectDir)
284         {
285         slnFile = projectDir;
286         slnFile += '/';
287         slnFile += projectName;
288         }
289       else
290         {
291         slnFile = projectName;
292         }
293       if (parser.ParseFile(slnFile + ".sln", slnData,
294                            cmVisualStudioSlnParser::DataGroupProjects))
295         {
296         if (cmSlnProjectEntry const* proj =
297             slnData.GetProjectByName(targetName))
298           {
299           targetProject = proj->GetRelativePath();
300           cmSystemTools::ConvertToUnixSlashes(targetProject);
301           }
302         }
303       }
304     makeCommand += " ";
305     makeCommand += targetProject;
306     makeCommand += " ";
307     }
308   makeCommand += "/p:Configuration=";
309   if(config && strlen(config))
310     {
311     makeCommand += config;
312     }
313   else
314     {
315     makeCommand += "Debug";
316     }
317   makeCommand += " /p:VisualStudioVersion=";
318   makeCommand += this->GetIDEVersion();
319   if ( additionalOptions )
320     {
321     makeCommand += " ";
322     makeCommand += additionalOptions;
323     }
324   return makeCommand;
325 }
326
327 //----------------------------------------------------------------------------
328 bool cmGlobalVisualStudio10Generator::Find64BitTools(cmMakefile* mf)
329 {
330   if(!this->PlatformToolset.empty())
331     {
332     return true;
333     }
334   // This edition does not come with 64-bit tools.  Look for them.
335   //
336   // TODO: Detect available tools?  x64\v100 exists but does not work?
337   // KHLM\\SOFTWARE\\Microsoft\\MSBuild\\ToolsVersions\\4.0;VCTargetsPath
338   // c:/Program Files (x86)/MSBuild/Microsoft.Cpp/v4.0/Platforms/
339   //   {Itanium,Win32,x64}/PlatformToolsets/{v100,v90,Windows7.1SDK}
340   std::string winSDK_7_1;
341   if(cmSystemTools::ReadRegistryValue(
342        "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Microsoft SDKs\\"
343        "Windows\\v7.1;InstallationFolder", winSDK_7_1))
344     {
345     cmOStringStream m;
346     m << "Found Windows SDK v7.1: " << winSDK_7_1;
347     mf->DisplayStatus(m.str().c_str(), -1);
348     this->PlatformToolset = "Windows7.1SDK";
349     return true;
350     }
351   else
352     {
353     cmOStringStream e;
354     e << "Cannot enable 64-bit tools with Visual Studio 2010 Express.\n"
355       << "Install the Microsoft Windows SDK v7.1 to get 64-bit tools:\n"
356       << "  http://msdn.microsoft.com/en-us/windows/bb980924.aspx";
357     mf->IssueMessage(cmake::FATAL_ERROR, e.str().c_str());
358     cmSystemTools::SetFatalErrorOccured();
359     return false;
360     }
361 }
362
363 //----------------------------------------------------------------------------
364 std::string
365 cmGlobalVisualStudio10Generator
366 ::GenerateRuleFile(std::string const& output) const
367 {
368   // The VS 10 generator needs to create the .rule files on disk.
369   // Hide them away under the CMakeFiles directory.
370   std::string ruleDir = this->GetCMakeInstance()->GetHomeOutputDirectory();
371   ruleDir += cmake::GetCMakeFilesDirectory();
372   ruleDir += "/";
373   ruleDir += cmSystemTools::ComputeStringMD5(
374     cmSystemTools::GetFilenamePath(output).c_str());
375   std::string ruleFile = ruleDir + "/";
376   ruleFile += cmSystemTools::GetFilenameName(output);
377   ruleFile += ".rule";
378   return ruleFile;
379 }
380
381 //----------------------------------------------------------------------------
382 void cmGlobalVisualStudio10Generator::PathTooLong(
383   cmTarget* target, cmSourceFile* sf, std::string const& sfRel)
384 {
385   size_t len = (strlen(target->GetMakefile()->GetCurrentOutputDirectory()) +
386                 1 + sfRel.length());
387   if(len > this->LongestSource.Length)
388     {
389     this->LongestSource.Length = len;
390     this->LongestSource.Target = target;
391     this->LongestSource.SourceFile = sf;
392     this->LongestSource.SourceRel = sfRel;
393     }
394 }
395
396 //----------------------------------------------------------------------------
397 bool cmGlobalVisualStudio10Generator::UseFolderProperty()
398 {
399   return IsExpressEdition() ? false : cmGlobalGenerator::UseFolderProperty();
400 }