resolve cyclic dependency with zstd
[platform/upstream/cmake.git] / Source / cmGlobalVisualStudio8Generator.cxx
1 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
2    file Copyright.txt or https://cmake.org/licensing for details.  */
3 #include "cmGlobalVisualStudio8Generator.h"
4
5 #include <algorithm>
6 #include <functional>
7 #include <ostream>
8 #include <utility>
9
10 #include <cm/memory>
11 #include <cmext/algorithm>
12 #include <cmext/memory>
13
14 #include "cmCustomCommand.h"
15 #include "cmCustomCommandLines.h"
16 #include "cmCustomCommandTypes.h"
17 #include "cmGeneratedFileStream.h"
18 #include "cmGeneratorExpression.h"
19 #include "cmGeneratorTarget.h"
20 #include "cmGlobalGenerator.h"
21 #include "cmGlobalVisualStudio7Generator.h"
22 #include "cmGlobalVisualStudioGenerator.h"
23 #include "cmListFileCache.h"
24 #include "cmLocalGenerator.h"
25 #include "cmLocalVisualStudio7Generator.h"
26 #include "cmMakefile.h"
27 #include "cmPolicies.h"
28 #include "cmSourceFile.h"
29 #include "cmStateTypes.h"
30 #include "cmStringAlgorithms.h"
31 #include "cmSystemTools.h"
32 #include "cmTarget.h"
33 #include "cmTargetDepend.h"
34 #include "cmValue.h"
35 #include "cmVisualStudioGeneratorOptions.h"
36 #include "cmake.h"
37
38 struct cmIDEFlagTable;
39
40 cmGlobalVisualStudio8Generator::cmGlobalVisualStudio8Generator(
41   cmake* cm, const std::string& name,
42   std::string const& platformInGeneratorName)
43   : cmGlobalVisualStudio71Generator(cm, platformInGeneratorName)
44 {
45   this->ProjectConfigurationSectionName = "ProjectConfigurationPlatforms";
46   this->Name = name;
47   this->ExtraFlagTable = this->GetExtraFlagTableVS8();
48 }
49
50 std::string cmGlobalVisualStudio8Generator::FindDevEnvCommand()
51 {
52   // First look for VCExpress.
53   std::string vsxcmd;
54   std::string vsxkey =
55     cmStrCat("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VCExpress\\",
56              this->GetIDEVersion(), ";InstallDir");
57   if (cmSystemTools::ReadRegistryValue(vsxkey.c_str(), vsxcmd,
58                                        cmSystemTools::KeyWOW64_32)) {
59     cmSystemTools::ConvertToUnixSlashes(vsxcmd);
60     vsxcmd += "/VCExpress.exe";
61     return vsxcmd;
62   }
63   // Now look for devenv.
64   return this->cmGlobalVisualStudio71Generator::FindDevEnvCommand();
65 }
66
67 void cmGlobalVisualStudio8Generator::EnableLanguage(
68   std::vector<std::string> const& lang, cmMakefile* mf, bool optional)
69 {
70   for (std::string const& it : lang) {
71     if (it == "ASM_MASM") {
72       this->MasmEnabled = true;
73     }
74   }
75   this->AddPlatformDefinitions(mf);
76   cmGlobalVisualStudio7Generator::EnableLanguage(lang, mf, optional);
77 }
78
79 void cmGlobalVisualStudio8Generator::AddPlatformDefinitions(cmMakefile* mf)
80 {
81   if (this->TargetsWindowsCE()) {
82     mf->AddDefinition("CMAKE_VS_WINCE_VERSION", this->WindowsCEVersion);
83   }
84 }
85
86 bool cmGlobalVisualStudio8Generator::SetGeneratorPlatform(std::string const& p,
87                                                           cmMakefile* mf)
88 {
89   if (this->PlatformInGeneratorName) {
90     // This is an old-style generator name that contains the platform name.
91     // No explicit platform specification is supported, so pass it through
92     // to our base class implementation, which errors on non-empty platforms.
93     return this->cmGlobalVisualStudio7Generator::SetGeneratorPlatform(p, mf);
94   }
95
96   this->GeneratorPlatform = p;
97
98   // FIXME: Add CMAKE_GENERATOR_PLATFORM field to set the framework.
99   // For now, just report the generator's default, if any.
100   if (cm::optional<std::string> const& targetFrameworkVersion =
101         this->GetTargetFrameworkVersion()) {
102     mf->AddDefinition("CMAKE_VS_TARGET_FRAMEWORK_VERSION",
103                       *targetFrameworkVersion);
104   }
105   if (cm::optional<std::string> const& targetFrameworkIdentifier =
106         this->GetTargetFrameworkIdentifier()) {
107     mf->AddDefinition("CMAKE_VS_TARGET_FRAMEWORK_IDENTIFIER",
108                       *targetFrameworkIdentifier);
109   }
110   if (cm::optional<std::string> const& targetFrameworkTargetsVersion =
111         this->GetTargetFrameworkTargetsVersion()) {
112     mf->AddDefinition("CMAKE_VS_TARGET_FRAMEWORK_TARGETS_VERSION",
113                       *targetFrameworkTargetsVersion);
114   }
115
116   // The generator name does not contain the platform name, and so supports
117   // explicit platform specification.  We handled that above, so pass an
118   // empty platform name to our base class implementation so it does not error.
119   return this->cmGlobalVisualStudio7Generator::SetGeneratorPlatform("", mf);
120 }
121
122 cm::optional<std::string> const&
123 cmGlobalVisualStudio8Generator::GetTargetFrameworkVersion() const
124 {
125   return this->DefaultTargetFrameworkVersion;
126 }
127
128 cm::optional<std::string> const&
129 cmGlobalVisualStudio8Generator::GetTargetFrameworkIdentifier() const
130 {
131   return this->DefaultTargetFrameworkIdentifier;
132 }
133
134 cm::optional<std::string> const&
135 cmGlobalVisualStudio8Generator::GetTargetFrameworkTargetsVersion() const
136 {
137   return this->DefaultTargetFrameworkTargetsVersion;
138 }
139
140 std::string cmGlobalVisualStudio8Generator::GetGenerateStampList()
141 {
142   return "generate.stamp.list";
143 }
144
145 void cmGlobalVisualStudio8Generator::Configure()
146 {
147   this->cmGlobalVisualStudio7Generator::Configure();
148 }
149
150 bool cmGlobalVisualStudio8Generator::UseFolderProperty() const
151 {
152   return IsExpressEdition() ? false : cmGlobalGenerator::UseFolderProperty();
153 }
154
155 bool cmGlobalVisualStudio8Generator::AddCheckTarget()
156 {
157   // Add a special target on which all other targets depend that
158   // checks the build system and optionally re-runs CMake.
159   // Skip the target if no regeneration is to be done.
160   if (this->GlobalSettingIsOn("CMAKE_SUPPRESS_REGENERATION")) {
161     return false;
162   }
163
164   std::vector<std::unique_ptr<cmLocalGenerator>> const& generators =
165     this->LocalGenerators;
166   auto& lg =
167     cm::static_reference_cast<cmLocalVisualStudio7Generator>(generators[0]);
168
169   auto cc = cm::make_unique<cmCustomCommand>();
170   cc->SetCMP0116Status(cmPolicies::NEW);
171   cmTarget* tgt = lg.AddUtilityCommand(CMAKE_CHECK_BUILD_SYSTEM_TARGET, false,
172                                        std::move(cc));
173
174   auto ptr = cm::make_unique<cmGeneratorTarget>(tgt, &lg);
175   auto gt = ptr.get();
176   lg.AddGeneratorTarget(std::move(ptr));
177
178   // Organize in the "predefined targets" folder:
179   //
180   if (this->UseFolderProperty()) {
181     tgt->SetProperty("FOLDER", this->GetPredefinedTargetsFolder());
182   }
183
184   // Create a list of all stamp files for this project.
185   std::vector<std::string> stamps;
186   std::string stampList = cmStrCat(
187     "CMakeFiles/", cmGlobalVisualStudio8Generator::GetGenerateStampList());
188   {
189     std::string stampListFile =
190       cmStrCat(generators[0]->GetMakefile()->GetCurrentBinaryDirectory(), '/',
191                stampList);
192     std::string stampFile;
193     cmGeneratedFileStream fout(stampListFile.c_str());
194     for (const auto& gi : generators) {
195       stampFile = cmStrCat(gi->GetMakefile()->GetCurrentBinaryDirectory(),
196                            "/CMakeFiles/generate.stamp");
197       fout << stampFile << "\n";
198       stamps.push_back(stampFile);
199     }
200   }
201
202   // Add a custom rule to re-run CMake if any input files changed.
203   {
204     // The custom rule runs cmake so set UTF-8 pipes.
205     bool stdPipesUTF8 = true;
206
207     // Collect the input files used to generate all targets in this
208     // project.
209     std::vector<std::string> listFiles;
210     for (const auto& gen : generators) {
211       cm::append(listFiles, gen->GetMakefile()->GetListFiles());
212     }
213
214     // Add a custom prebuild target to run the VerifyGlobs script.
215     cmake* cm = this->GetCMakeInstance();
216     if (cm->DoWriteGlobVerifyTarget()) {
217       cmCustomCommandLines verifyCommandLines = cmMakeSingleCommandLine(
218         { cmSystemTools::GetCMakeCommand(), "-P", cm->GetGlobVerifyScript() });
219       std::vector<std::string> byproducts;
220       byproducts.push_back(cm->GetGlobVerifyStamp());
221
222       cc = cm::make_unique<cmCustomCommand>();
223       cc->SetByproducts(byproducts);
224       cc->SetCommandLines(verifyCommandLines);
225       cc->SetComment("Checking File Globs");
226       cc->SetCMP0116Status(cmPolicies::NEW);
227       cc->SetStdPipesUTF8(stdPipesUTF8);
228       lg.AddCustomCommandToTarget(CMAKE_CHECK_BUILD_SYSTEM_TARGET,
229                                   cmCustomCommandType::PRE_BUILD,
230                                   std::move(cc));
231
232       // Ensure ZERO_CHECK always runs in Visual Studio using MSBuild,
233       // otherwise the prebuild command will not be run.
234       tgt->SetProperty("VS_GLOBAL_DisableFastUpToDateCheck", "true");
235       listFiles.push_back(cm->GetGlobVerifyStamp());
236     }
237
238     // Sort the list of input files and remove duplicates.
239     std::sort(listFiles.begin(), listFiles.end(), std::less<std::string>());
240     std::vector<std::string>::iterator new_end =
241       std::unique(listFiles.begin(), listFiles.end());
242     listFiles.erase(new_end, listFiles.end());
243
244     // Create a rule to re-run CMake.
245     std::string argS = cmStrCat("-S", lg.GetSourceDirectory());
246     std::string argB = cmStrCat("-B", lg.GetBinaryDirectory());
247     std::string const sln =
248       lg.GetBinaryDirectory() + "/" + lg.GetProjectName() + ".sln";
249     cmCustomCommandLines commandLines = cmMakeSingleCommandLine(
250       { cmSystemTools::GetCMakeCommand(), argS, argB, "--check-stamp-list",
251         stampList, "--vs-solution-file", sln });
252
253     // Add the rule.  Note that we cannot use the CMakeLists.txt
254     // file as the main dependency because it would get
255     // overwritten by the CreateVCProjBuildRule.
256     // (this could be avoided with per-target source files)
257     cc = cm::make_unique<cmCustomCommand>();
258     cc->SetOutputs(stamps);
259     cc->SetDepends(listFiles);
260     cc->SetCommandLines(commandLines);
261     cc->SetComment("Checking Build System");
262     cc->SetCMP0116Status(cmPolicies::NEW);
263     cc->SetEscapeOldStyle(false);
264     cc->SetStdPipesUTF8(stdPipesUTF8);
265     if (cmSourceFile* file =
266           lg.AddCustomCommandToOutput(std::move(cc), true)) {
267       gt->AddSource(file->ResolveFullPath());
268     } else {
269       cmSystemTools::Error("Error adding rule for " + stamps[0]);
270     }
271   }
272
273   return true;
274 }
275
276 void cmGlobalVisualStudio8Generator::AddExtraIDETargets()
277 {
278   cmGlobalVisualStudio7Generator::AddExtraIDETargets();
279   if (this->AddCheckTarget()) {
280     for (unsigned int i = 0; i < this->LocalGenerators.size(); ++i) {
281       const auto& tgts = this->LocalGenerators[i]->GetGeneratorTargets();
282       // All targets depend on the build-system check target.
283       for (const auto& ti : tgts) {
284         if (ti->GetName() != CMAKE_CHECK_BUILD_SYSTEM_TARGET) {
285           ti->Target->AddUtility(CMAKE_CHECK_BUILD_SYSTEM_TARGET, false);
286         }
287       }
288     }
289   }
290 }
291
292 void cmGlobalVisualStudio8Generator::WriteSolutionConfigurations(
293   std::ostream& fout, std::vector<std::string> const& configs)
294 {
295   fout << "\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n";
296   for (std::string const& i : configs) {
297     fout << "\t\t" << i << "|" << this->GetPlatformName() << " = " << i << "|"
298          << this->GetPlatformName() << "\n";
299   }
300   fout << "\tEndGlobalSection\n";
301 }
302
303 void cmGlobalVisualStudio8Generator::WriteProjectConfigurations(
304   std::ostream& fout, const std::string& name, cmGeneratorTarget const& target,
305   std::vector<std::string> const& configs,
306   const std::set<std::string>& configsPartOfDefaultBuild,
307   std::string const& platformMapping)
308 {
309   std::string guid = this->GetGUID(name);
310   for (std::string const& i : configs) {
311     std::vector<std::string> mapConfig;
312     const char* dstConfig = i.c_str();
313     if (target.GetProperty("EXTERNAL_MSPROJECT")) {
314       if (cmValue m = target.GetProperty("MAP_IMPORTED_CONFIG_" +
315                                          cmSystemTools::UpperCase(i))) {
316         cmExpandList(*m, mapConfig);
317         if (!mapConfig.empty()) {
318           dstConfig = mapConfig[0].c_str();
319         }
320       }
321     }
322     fout << "\t\t{" << guid << "}." << i << "|" << this->GetPlatformName()
323          << ".ActiveCfg = " << dstConfig << "|"
324          << (!platformMapping.empty() ? platformMapping
325                                       : this->GetPlatformName())
326          << "\n";
327     std::set<std::string>::const_iterator ci =
328       configsPartOfDefaultBuild.find(i);
329     if (!(ci == configsPartOfDefaultBuild.end())) {
330       fout << "\t\t{" << guid << "}." << i << "|" << this->GetPlatformName()
331            << ".Build.0 = " << dstConfig << "|"
332            << (!platformMapping.empty() ? platformMapping
333                                         : this->GetPlatformName())
334            << "\n";
335     }
336     if (this->NeedsDeploy(target, dstConfig)) {
337       fout << "\t\t{" << guid << "}." << i << "|" << this->GetPlatformName()
338            << ".Deploy.0 = " << dstConfig << "|"
339            << (!platformMapping.empty() ? platformMapping
340                                         : this->GetPlatformName())
341            << "\n";
342     }
343   }
344 }
345
346 bool cmGlobalVisualStudio8Generator::NeedsDeploy(
347   cmGeneratorTarget const& target, const char* config) const
348 {
349   cmStateEnums::TargetType const type = target.GetType();
350   if (type != cmStateEnums::EXECUTABLE &&
351       type != cmStateEnums::SHARED_LIBRARY) {
352     // deployment only valid on executables and shared libraries.
353     return false;
354   }
355
356   if (cmValue prop = target.GetProperty("VS_SOLUTION_DEPLOY")) {
357     // If set, it dictates behavior
358     return cmIsOn(
359       cmGeneratorExpression::Evaluate(*prop, target.LocalGenerator, config));
360   }
361
362   // To be deprecated, disable deployment even if target supports it.
363   if (cmValue prop = target.GetProperty("VS_NO_SOLUTION_DEPLOY")) {
364     if (cmIsOn(cmGeneratorExpression::Evaluate(*prop, target.LocalGenerator,
365                                                config))) {
366       // If true, always disable deployment
367       return false;
368     }
369   }
370
371   // Legacy behavior, enabled deployment based on 'hard-coded' target
372   // platforms.
373   return this->TargetSystemSupportsDeployment();
374 }
375
376 bool cmGlobalVisualStudio8Generator::TargetSystemSupportsDeployment() const
377 {
378   return this->TargetsWindowsCE();
379 }
380
381 bool cmGlobalVisualStudio8Generator::ComputeTargetDepends()
382 {
383   // Skip over the cmGlobalVisualStudioGenerator implementation!
384   // We do not need the support that VS <= 7.1 needs.
385   return this->cmGlobalGenerator::ComputeTargetDepends();
386 }
387
388 void cmGlobalVisualStudio8Generator::WriteProjectDepends(
389   std::ostream& fout, const std::string&, const std::string&,
390   cmGeneratorTarget const* gt)
391 {
392   TargetDependSet const& unordered = this->GetTargetDirectDepends(gt);
393   OrderedTargetDependSet depends(unordered, std::string());
394   for (cmTargetDepend const& i : depends) {
395     if (!this->IsInSolution(i)) {
396       continue;
397     }
398     std::string guid = this->GetGUID(i->GetName());
399     fout << "\t\t{" << guid << "} = {" << guid << "}\n";
400   }
401 }
402
403 bool cmGlobalVisualStudio8Generator::NeedLinkLibraryDependencies(
404   cmGeneratorTarget* target)
405 {
406   // Look for utility dependencies that magically link.
407   for (BT<std::pair<std::string, bool>> const& ui : target->GetUtilities()) {
408     if (cmGeneratorTarget* depTarget =
409           target->GetLocalGenerator()->FindGeneratorTargetToUse(
410             ui.Value.first)) {
411       if (depTarget->IsInBuildSystem() &&
412           depTarget->GetProperty("EXTERNAL_MSPROJECT")) {
413         // This utility dependency names an external .vcproj target.
414         // We use LinkLibraryDependencies="true" to link to it without
415         // predicting the .lib file location or name.
416         return true;
417       }
418     }
419   }
420   return false;
421 }
422
423 static cmVS7FlagTable cmVS8ExtraFlagTable[] = {
424   { "CallingConvention", "Gd", "cdecl", "0", 0 },
425   { "CallingConvention", "Gr", "fastcall", "1", 0 },
426   { "CallingConvention", "Gz", "stdcall", "2", 0 },
427
428   { "Detect64BitPortabilityProblems", "Wp64",
429     "Detect 64Bit Portability Problems", "true", 0 },
430   { "ErrorReporting", "errorReport:prompt", "Report immediately", "1", 0 },
431   { "ErrorReporting", "errorReport:queue", "Queue for next login", "2", 0 },
432   // Precompiled header and related options.  Note that the
433   // UsePrecompiledHeader entries are marked as "Continue" so that the
434   // corresponding PrecompiledHeaderThrough entry can be found.
435   { "UsePrecompiledHeader", "Yu", "Use Precompiled Header", "2",
436     cmVS7FlagTable::UserValueIgnored | cmVS7FlagTable::Continue },
437   { "PrecompiledHeaderThrough", "Yu", "Precompiled Header Name", "",
438     cmVS7FlagTable::UserValueRequired },
439   { "UsePrecompiledHeader", "Y-", "Don't use precompiled header", "0", 0 },
440   // There is no YX option in the VS8 IDE.
441
442   // Exception handling mode.  If no entries match, it will be FALSE.
443   { "ExceptionHandling", "GX", "enable c++ exceptions", "1", 0 },
444   { "ExceptionHandling", "EHsc", "enable c++ exceptions", "1", 0 },
445   { "ExceptionHandling", "EHa", "enable SEH exceptions", "2", 0 },
446
447   { "EnablePREfast", "analyze", "", "true", 0 },
448   { "EnablePREfast", "analyze-", "", "false", 0 },
449
450   // Language options
451   { "TreatWChar_tAsBuiltInType", "Zc:wchar_t", "wchar_t is a built-in type",
452     "true", 0 },
453   { "TreatWChar_tAsBuiltInType", "Zc:wchar_t-",
454     "wchar_t is not a built-in type", "false", 0 },
455
456   { "", "", "", "", 0 }
457 };
458 cmIDEFlagTable const* cmGlobalVisualStudio8Generator::GetExtraFlagTableVS8()
459 {
460   return cmVS8ExtraFlagTable;
461 }