summaryrefslogtreecommitdiffstats
path: root/Source/cmGlobalGenerator.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'Source/cmGlobalGenerator.cxx')
-rw-r--r--Source/cmGlobalGenerator.cxx3218
1 files changed, 3218 insertions, 0 deletions
diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx
new file mode 100644
index 0000000..ccbbf53
--- /dev/null
+++ b/Source/cmGlobalGenerator.cxx
@@ -0,0 +1,3218 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#include "cmGlobalGenerator.h"
+
+#include <algorithm>
+#include <cassert>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <functional>
+#include <initializer_list>
+#include <iterator>
+#include <sstream>
+#include <utility>
+
+#include <cm/memory>
+
+#include "cmsys/Directory.hxx"
+#include "cmsys/FStream.hxx"
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+# include <windows.h>
+#endif
+
+#include "cmAlgorithms.h"
+#include "cmCPackPropertiesGenerator.h"
+#include "cmComputeTargetDepends.h"
+#include "cmCustomCommand.h"
+#include "cmCustomCommandLines.h"
+#include "cmDuration.h"
+#include "cmExportBuildFileGenerator.h"
+#include "cmExternalMakefileProjectGenerator.h"
+#include "cmGeneratedFileStream.h"
+#include "cmGeneratorExpression.h"
+#include "cmGeneratorTarget.h"
+#include "cmInstallGenerator.h"
+#include "cmLinkLineComputer.h"
+#include "cmListFileCache.h"
+#include "cmLocalGenerator.h"
+#include "cmMSVC60LinkLineComputer.h"
+#include "cmMakefile.h"
+#include "cmMessageType.h"
+#include "cmPolicies.h"
+#include "cmRange.h"
+#include "cmSourceFile.h"
+#include "cmState.h"
+#include "cmStateDirectory.h"
+#include "cmStateTypes.h"
+#include "cmVersion.h"
+#include "cmWorkingDirectory.h"
+#include "cmake.h"
+
+#if !defined(CMAKE_BOOTSTRAP)
+# include "cm_jsoncpp_value.h"
+# include "cm_jsoncpp_writer.h"
+
+# include "cmCryptoHash.h"
+# include "cmQtAutoGenGlobalInitializer.h"
+#endif
+
+#if defined(_MSC_VER) && _MSC_VER >= 1800
+# define KWSYS_WINDOWS_DEPRECATED_GetVersionEx
+#endif
+
+const std::string kCMAKE_PLATFORM_INFO_INITIALIZED =
+ "CMAKE_PLATFORM_INFO_INITIALIZED";
+
+class cmInstalledFile;
+
+bool cmTarget::StrictTargetComparison::operator()(cmTarget const* t1,
+ cmTarget const* t2) const
+{
+ int nameResult = strcmp(t1->GetName().c_str(), t2->GetName().c_str());
+ if (nameResult == 0) {
+ return strcmp(t1->GetMakefile()->GetCurrentBinaryDirectory().c_str(),
+ t2->GetMakefile()->GetCurrentBinaryDirectory().c_str()) < 0;
+ }
+ return nameResult < 0;
+}
+
+cmGlobalGenerator::cmGlobalGenerator(cmake* cm)
+ : CMakeInstance(cm)
+{
+ // By default the .SYMBOLIC dependency is not needed on symbolic rules.
+ this->NeedSymbolicMark = false;
+
+ // by default use the native paths
+ this->ForceUnixPaths = false;
+
+ // By default do not try to support color.
+ this->ToolSupportsColor = false;
+
+ // By default do not use link scripts.
+ this->UseLinkScript = false;
+
+ // Whether an install target is needed.
+ this->InstallTargetEnabled = false;
+
+ // how long to let try compiles run
+ this->TryCompileTimeout = cmDuration::zero();
+
+ this->CurrentConfigureMakefile = nullptr;
+ this->TryCompileOuterMakefile = nullptr;
+
+ this->ConfigureDoneCMP0026AndCMP0024 = false;
+ this->FirstTimeProgress = 0.0f;
+
+ this->RecursionDepth = 0;
+
+ cm->GetState()->SetIsGeneratorMultiConfig(false);
+ cm->GetState()->SetMinGWMake(false);
+ cm->GetState()->SetMSYSShell(false);
+ cm->GetState()->SetNMake(false);
+ cm->GetState()->SetWatcomWMake(false);
+ cm->GetState()->SetWindowsShell(false);
+ cm->GetState()->SetWindowsVSIDE(false);
+}
+
+cmGlobalGenerator::~cmGlobalGenerator()
+{
+ this->ClearGeneratorMembers();
+}
+
+#if !defined(CMAKE_BOOTSTRAP)
+Json::Value cmGlobalGenerator::GetJson() const
+{
+ Json::Value generator = Json::objectValue;
+ generator["name"] = this->GetName();
+ generator["multiConfig"] = this->IsMultiConfig();
+ return generator;
+}
+#endif
+
+bool cmGlobalGenerator::SetGeneratorInstance(std::string const& i,
+ cmMakefile* mf)
+{
+ if (i.empty()) {
+ return true;
+ }
+
+ std::ostringstream e;
+ /* clang-format off */
+ e <<
+ "Generator\n"
+ " " << this->GetName() << "\n"
+ "does not support instance specification, but instance\n"
+ " " << i << "\n"
+ "was specified.";
+ /* clang-format on */
+ mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
+ return false;
+}
+
+bool cmGlobalGenerator::SetGeneratorPlatform(std::string const& p,
+ cmMakefile* mf)
+{
+ if (p.empty()) {
+ return true;
+ }
+
+ std::ostringstream e;
+ /* clang-format off */
+ e <<
+ "Generator\n"
+ " " << this->GetName() << "\n"
+ "does not support platform specification, but platform\n"
+ " " << p << "\n"
+ "was specified.";
+ /* clang-format on */
+ mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
+ return false;
+}
+
+bool cmGlobalGenerator::SetGeneratorToolset(std::string const& ts, bool,
+ cmMakefile* mf)
+{
+ if (ts.empty()) {
+ return true;
+ }
+ std::ostringstream e;
+ /* clang-format off */
+ e <<
+ "Generator\n"
+ " " << this->GetName() << "\n"
+ "does not support toolset specification, but toolset\n"
+ " " << ts << "\n"
+ "was specified.";
+ /* clang-format on */
+ mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
+ return false;
+}
+
+std::string cmGlobalGenerator::SelectMakeProgram(
+ const std::string& inMakeProgram, const std::string& makeDefault) const
+{
+ std::string makeProgram = inMakeProgram;
+ if (cmIsOff(makeProgram)) {
+ const char* makeProgramCSTR =
+ this->CMakeInstance->GetCacheDefinition("CMAKE_MAKE_PROGRAM");
+ if (cmIsOff(makeProgramCSTR)) {
+ makeProgram = makeDefault;
+ } else {
+ makeProgram = makeProgramCSTR;
+ }
+ if (cmIsOff(makeProgram) && !makeProgram.empty()) {
+ makeProgram = "CMAKE_MAKE_PROGRAM-NOTFOUND";
+ }
+ }
+ return makeProgram;
+}
+
+void cmGlobalGenerator::ResolveLanguageCompiler(const std::string& lang,
+ cmMakefile* mf,
+ bool optional) const
+{
+ std::string langComp = cmStrCat("CMAKE_", lang, "_COMPILER");
+
+ if (!mf->GetDefinition(langComp)) {
+ if (!optional) {
+ cmSystemTools::Error(langComp + " not set, after EnableLanguage");
+ }
+ return;
+ }
+ std::string const& name = mf->GetRequiredDefinition(langComp);
+ std::string path;
+ if (!cmSystemTools::FileIsFullPath(name)) {
+ path = cmSystemTools::FindProgram(name);
+ } else {
+ path = name;
+ }
+ if (!optional && (path.empty() || !cmSystemTools::FileExists(path))) {
+ return;
+ }
+ const std::string* cname =
+ this->GetCMakeInstance()->GetState()->GetInitializedCacheValue(langComp);
+ std::string changeVars;
+ if (cname && !optional) {
+ std::string cnameString;
+ if (!cmSystemTools::FileIsFullPath(*cname)) {
+ cnameString = cmSystemTools::FindProgram(*cname);
+ } else {
+ cnameString = *cname;
+ }
+ std::string pathString = path;
+ // get rid of potentially multiple slashes:
+ cmSystemTools::ConvertToUnixSlashes(cnameString);
+ cmSystemTools::ConvertToUnixSlashes(pathString);
+ if (cnameString != pathString) {
+ cmProp cvars = this->GetCMakeInstance()->GetState()->GetGlobalProperty(
+ "__CMAKE_DELETE_CACHE_CHANGE_VARS_");
+ if (cvars) {
+ changeVars += *cvars;
+ changeVars += ";";
+ }
+ changeVars += langComp;
+ changeVars += ";";
+ changeVars += *cname;
+ this->GetCMakeInstance()->GetState()->SetGlobalProperty(
+ "__CMAKE_DELETE_CACHE_CHANGE_VARS_", changeVars.c_str());
+ }
+ }
+}
+
+void cmGlobalGenerator::AddBuildExportSet(cmExportBuildFileGenerator* gen)
+{
+ this->BuildExportSets[gen->GetMainExportFileName()] = gen;
+}
+
+void cmGlobalGenerator::AddBuildExportExportSet(
+ cmExportBuildFileGenerator* gen)
+{
+ this->BuildExportExportSets[gen->GetMainExportFileName()] = gen;
+ this->AddBuildExportSet(gen);
+}
+
+bool cmGlobalGenerator::GenerateImportFile(const std::string& file)
+{
+ auto const it = this->BuildExportSets.find(file);
+ if (it != this->BuildExportSets.end()) {
+ bool result = it->second->GenerateImportFile();
+
+ if (!this->ConfigureDoneCMP0026AndCMP0024) {
+ for (const auto& m : this->Makefiles) {
+ m->RemoveExportBuildFileGeneratorCMP0024(it->second);
+ }
+ }
+
+ this->BuildExportSets.erase(it);
+ return result;
+ }
+ return false;
+}
+
+void cmGlobalGenerator::ForceLinkerLanguages()
+{
+}
+
+bool cmGlobalGenerator::CheckTargetsForMissingSources() const
+{
+ bool failed = false;
+ for (const auto& localGen : this->LocalGenerators) {
+ for (const auto& target : localGen->GetGeneratorTargets()) {
+ if (target->GetType() == cmStateEnums::TargetType::GLOBAL_TARGET ||
+ target->GetType() == cmStateEnums::TargetType::INTERFACE_LIBRARY ||
+ target->GetType() == cmStateEnums::TargetType::UTILITY ||
+ cmIsOn(target->GetProperty("ghs_integrity_app"))) {
+ continue;
+ }
+
+ std::vector<std::string> configs;
+ target->Makefile->GetConfigurations(configs);
+ std::vector<cmSourceFile*> srcs;
+ if (configs.empty()) {
+ target->GetSourceFiles(srcs, "");
+ } else {
+ for (std::string const& config : configs) {
+ target->GetSourceFiles(srcs, config);
+ if (srcs.empty()) {
+ break;
+ }
+ }
+ }
+ if (srcs.empty()) {
+ std::ostringstream e;
+ e << "No SOURCES given to target: " << target->GetName();
+ this->GetCMakeInstance()->IssueMessage(
+ MessageType::FATAL_ERROR, e.str(), target->GetBacktrace());
+ failed = true;
+ }
+ }
+ }
+ return failed;
+}
+
+bool cmGlobalGenerator::CheckTargetsForType() const
+{
+ if (!this->GetLanguageEnabled("Swift")) {
+ return false;
+ }
+ bool failed = false;
+ for (const auto& generator : this->LocalGenerators) {
+ for (const auto& target : generator->GetGeneratorTargets()) {
+ if (target->GetType() == cmStateEnums::EXECUTABLE &&
+ target->GetPropertyAsBool("WIN32_EXECUTABLE")) {
+ std::vector<std::string> const& configs =
+ target->Makefile->GetGeneratorConfigs();
+ for (std::string const& config : configs) {
+ if (target->GetLinkerLanguage(config) == "Swift") {
+ this->GetCMakeInstance()->IssueMessage(
+ MessageType::FATAL_ERROR,
+ "WIN32_EXECUTABLE property is not supported on Swift "
+ "executables",
+ target->GetBacktrace());
+ failed = true;
+ }
+ }
+ }
+ }
+ }
+ return failed;
+}
+
+bool cmGlobalGenerator::CheckTargetsForPchCompilePdb() const
+{
+ if (!this->GetLanguageEnabled("C") && !this->GetLanguageEnabled("CXX")) {
+ return false;
+ }
+ bool failed = false;
+ for (const auto& generator : this->LocalGenerators) {
+ for (const auto& target : generator->GetGeneratorTargets()) {
+ if (target->GetType() == cmStateEnums::TargetType::GLOBAL_TARGET ||
+ target->GetType() == cmStateEnums::TargetType::INTERFACE_LIBRARY ||
+ target->GetType() == cmStateEnums::TargetType::UTILITY ||
+ cmIsOn(target->GetProperty("ghs_integrity_app"))) {
+ continue;
+ }
+
+ const std::string reuseFrom =
+ target->GetSafeProperty("PRECOMPILE_HEADERS_REUSE_FROM");
+ const std::string compilePdb =
+ target->GetSafeProperty("COMPILE_PDB_NAME");
+
+ if (!reuseFrom.empty() && reuseFrom != compilePdb) {
+ const std::string e = cmStrCat(
+ "PRECOMPILE_HEADERS_REUSE_FROM property is set on target (\"",
+ target->GetName(),
+ "\"). Reusable precompile headers requires the COMPILE_PDB_NAME"
+ " property to have the value \"",
+ reuseFrom, "\"\n");
+ this->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR, e,
+ target->GetBacktrace());
+ failed = true;
+ }
+ }
+ }
+ return failed;
+}
+
+bool cmGlobalGenerator::IsExportedTargetsFile(
+ const std::string& filename) const
+{
+ auto const it = this->BuildExportSets.find(filename);
+ if (it == this->BuildExportSets.end()) {
+ return false;
+ }
+ return !cmContains(this->BuildExportExportSets, filename);
+}
+
+// Find the make program for the generator, required for try compiles
+bool cmGlobalGenerator::FindMakeProgram(cmMakefile* mf)
+{
+ if (this->FindMakeProgramFile.empty()) {
+ cmSystemTools::Error(
+ "Generator implementation error, "
+ "all generators must specify this->FindMakeProgramFile");
+ return false;
+ }
+ if (!mf->GetDefinition("CMAKE_MAKE_PROGRAM") ||
+ cmIsOff(mf->GetDefinition("CMAKE_MAKE_PROGRAM"))) {
+ std::string setMakeProgram = mf->GetModulesFile(this->FindMakeProgramFile);
+ if (!setMakeProgram.empty()) {
+ mf->ReadListFile(setMakeProgram);
+ }
+ }
+ if (!mf->GetDefinition("CMAKE_MAKE_PROGRAM") ||
+ cmIsOff(mf->GetDefinition("CMAKE_MAKE_PROGRAM"))) {
+ std::ostringstream err;
+ err << "CMake was unable to find a build program corresponding to \""
+ << this->GetName() << "\". CMAKE_MAKE_PROGRAM is not set. You "
+ << "probably need to select a different build tool.";
+ cmSystemTools::Error(err.str());
+ cmSystemTools::SetFatalErrorOccured();
+ return false;
+ }
+ std::string makeProgram = mf->GetRequiredDefinition("CMAKE_MAKE_PROGRAM");
+ // if there are spaces in the make program use short path
+ // but do not short path the actual program name, as
+ // this can cause trouble with VSExpress
+ if (makeProgram.find(' ') != std::string::npos) {
+ std::string dir;
+ std::string file;
+ cmSystemTools::SplitProgramPath(makeProgram, dir, file);
+ std::string saveFile = file;
+ cmSystemTools::GetShortPath(makeProgram, makeProgram);
+ cmSystemTools::SplitProgramPath(makeProgram, dir, file);
+ makeProgram = cmStrCat(dir, '/', saveFile);
+ mf->AddCacheDefinition("CMAKE_MAKE_PROGRAM", makeProgram, "make program",
+ cmStateEnums::FILEPATH);
+ }
+ return true;
+}
+
+bool cmGlobalGenerator::CheckLanguages(
+ std::vector<std::string> const& /* languages */, cmMakefile* /* mf */) const
+{
+ return true;
+}
+
+// enable the given language
+//
+// The following files are loaded in this order:
+//
+// First figure out what OS we are running on:
+//
+// CMakeSystem.cmake - configured file created by CMakeDetermineSystem.cmake
+// CMakeDetermineSystem.cmake - figure out os info and create
+// CMakeSystem.cmake IF CMAKE_SYSTEM
+// not set
+// CMakeSystem.cmake - configured file created by
+// CMakeDetermineSystem.cmake IF CMAKE_SYSTEM_LOADED
+
+// CMakeSystemSpecificInitialize.cmake
+// - includes Platform/${CMAKE_SYSTEM_NAME}-Initialize.cmake
+
+// Next try and enable all languages found in the languages vector
+//
+// FOREACH LANG in languages
+// CMake(LANG)Compiler.cmake - configured file create by
+// CMakeDetermine(LANG)Compiler.cmake
+// CMakeDetermine(LANG)Compiler.cmake - Finds compiler for LANG and
+// creates CMake(LANG)Compiler.cmake
+// CMake(LANG)Compiler.cmake - configured file created by
+// CMakeDetermine(LANG)Compiler.cmake
+//
+// CMakeSystemSpecificInformation.cmake
+// - includes Platform/${CMAKE_SYSTEM_NAME}.cmake
+// may use compiler stuff
+
+// FOREACH LANG in languages
+// CMake(LANG)Information.cmake
+// - loads Platform/${CMAKE_SYSTEM_NAME}-${COMPILER}.cmake
+// CMakeTest(LANG)Compiler.cmake
+// - Make sure the compiler works with a try compile if
+// CMakeDetermine(LANG) was loaded
+//
+// Now load a few files that can override values set in any of the above
+// (PROJECTNAME)Compatibility.cmake
+// - load any backwards compatibility stuff for current project
+// ${CMAKE_USER_MAKE_RULES_OVERRIDE}
+// - allow users a chance to override system variables
+//
+//
+
+void cmGlobalGenerator::EnableLanguage(
+ std::vector<std::string> const& languages, cmMakefile* mf, bool optional)
+{
+ if (languages.empty()) {
+ cmSystemTools::Error("EnableLanguage must have a lang specified!");
+ cmSystemTools::SetFatalErrorOccured();
+ return;
+ }
+
+ std::set<std::string> cur_languages(languages.begin(), languages.end());
+ for (std::string const& li : cur_languages) {
+ if (!this->LanguagesInProgress.insert(li).second) {
+ std::ostringstream e;
+ e << "Language '" << li
+ << "' is currently being enabled. "
+ "Recursive call not allowed.";
+ mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
+ cmSystemTools::SetFatalErrorOccured();
+ return;
+ }
+ }
+
+ if (this->TryCompileOuterMakefile) {
+ // In a try-compile we can only enable languages provided by caller.
+ for (std::string const& lang : languages) {
+ if (lang == "NONE") {
+ this->SetLanguageEnabled("NONE", mf);
+ } else {
+ if (!cmContains(this->LanguagesReady, lang)) {
+ std::ostringstream e;
+ e << "The test project needs language " << lang
+ << " which is not enabled.";
+ this->TryCompileOuterMakefile->IssueMessage(MessageType::FATAL_ERROR,
+ e.str());
+ cmSystemTools::SetFatalErrorOccured();
+ return;
+ }
+ }
+ }
+ }
+
+ bool fatalError = false;
+
+ mf->AddDefinitionBool("RUN_CONFIGURE", true);
+ std::string rootBin =
+ cmStrCat(this->CMakeInstance->GetHomeOutputDirectory(), "/CMakeFiles");
+
+ // If the configuration files path has been set,
+ // then we are in a try compile and need to copy the enable language
+ // files from the parent cmake bin dir, into the try compile bin dir
+ if (!this->ConfiguredFilesPath.empty()) {
+ rootBin = this->ConfiguredFilesPath;
+ }
+ rootBin += '/';
+ rootBin += cmVersion::GetCMakeVersion();
+
+ // set the dir for parent files so they can be used by modules
+ mf->AddDefinition("CMAKE_PLATFORM_INFO_DIR", rootBin);
+
+ if (!this->CMakeInstance->GetIsInTryCompile()) {
+ // Keep a mark in the cache to indicate that we've initialized the
+ // platform information directory. If the platform information
+ // directory exists but the mark is missing then CMakeCache.txt
+ // has been removed or replaced without also removing the CMakeFiles/
+ // directory. In this case remove the platform information directory
+ // so that it will be re-initialized and the relevant information
+ // restored in the cache.
+ if (cmSystemTools::FileIsDirectory(rootBin) &&
+ !mf->IsOn(kCMAKE_PLATFORM_INFO_INITIALIZED)) {
+ cmSystemTools::RemoveADirectory(rootBin);
+ }
+ this->GetCMakeInstance()->AddCacheEntry(
+ kCMAKE_PLATFORM_INFO_INITIALIZED, "1",
+ "Platform information initialized", cmStateEnums::INTERNAL);
+ }
+
+ // try and load the CMakeSystem.cmake if it is there
+ std::string fpath = rootBin;
+ bool const readCMakeSystem = !mf->GetDefinition("CMAKE_SYSTEM_LOADED");
+ if (readCMakeSystem) {
+ fpath += "/CMakeSystem.cmake";
+ if (cmSystemTools::FileExists(fpath)) {
+ mf->ReadListFile(fpath);
+ }
+ }
+ // Load the CMakeDetermineSystem.cmake file and find out
+ // what platform we are running on
+ if (!mf->GetDefinition("CMAKE_SYSTEM")) {
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ /* Windows version number data. */
+ OSVERSIONINFOEXW osviex;
+ ZeroMemory(&osviex, sizeof(osviex));
+ osviex.dwOSVersionInfoSize = sizeof(osviex);
+
+# ifdef KWSYS_WINDOWS_DEPRECATED_GetVersionEx
+# pragma warning(push)
+# ifdef __INTEL_COMPILER
+# pragma warning(disable : 1478)
+# elif defined __clang__
+# pragma clang diagnostic push
+# pragma clang diagnostic ignored "-Wdeprecated-declarations"
+# else
+# pragma warning(disable : 4996)
+# endif
+# endif
+ GetVersionExW((OSVERSIONINFOW*)&osviex);
+# ifdef KWSYS_WINDOWS_DEPRECATED_GetVersionEx
+# ifdef __clang__
+# pragma clang diagnostic pop
+# else
+# pragma warning(pop)
+# endif
+# endif
+ std::ostringstream windowsVersionString;
+ windowsVersionString << osviex.dwMajorVersion << "."
+ << osviex.dwMinorVersion << "."
+ << osviex.dwBuildNumber;
+ mf->AddDefinition("CMAKE_HOST_SYSTEM_VERSION", windowsVersionString.str());
+#endif
+ // Read the DetermineSystem file
+ std::string systemFile = mf->GetModulesFile("CMakeDetermineSystem.cmake");
+ mf->ReadListFile(systemFile);
+ // load the CMakeSystem.cmake from the binary directory
+ // this file is configured by the CMakeDetermineSystem.cmake file
+ fpath = cmStrCat(rootBin, "/CMakeSystem.cmake");
+ mf->ReadListFile(fpath);
+ }
+
+ if (readCMakeSystem) {
+ // Tell the generator about the instance, if any.
+ std::string instance = mf->GetSafeDefinition("CMAKE_GENERATOR_INSTANCE");
+ if (!this->SetGeneratorInstance(instance, mf)) {
+ cmSystemTools::SetFatalErrorOccured();
+ return;
+ }
+
+ // Tell the generator about the target system.
+ std::string system = mf->GetSafeDefinition("CMAKE_SYSTEM_NAME");
+ if (!this->SetSystemName(system, mf)) {
+ cmSystemTools::SetFatalErrorOccured();
+ return;
+ }
+
+ // Tell the generator about the platform, if any.
+ std::string platform = mf->GetSafeDefinition("CMAKE_GENERATOR_PLATFORM");
+ if (!this->SetGeneratorPlatform(platform, mf)) {
+ cmSystemTools::SetFatalErrorOccured();
+ return;
+ }
+
+ // Tell the generator about the toolset, if any.
+ std::string toolset = mf->GetSafeDefinition("CMAKE_GENERATOR_TOOLSET");
+ if (!this->SetGeneratorToolset(toolset, false, mf)) {
+ cmSystemTools::SetFatalErrorOccured();
+ return;
+ }
+
+ // Find the native build tool for this generator.
+ if (!this->FindMakeProgram(mf)) {
+ return;
+ }
+ }
+
+ // Check that the languages are supported by the generator and its
+ // native build tool found above.
+ if (!this->CheckLanguages(languages, mf)) {
+ return;
+ }
+
+ // **** Load the system specific initialization if not yet loaded
+ if (!mf->GetDefinition("CMAKE_SYSTEM_SPECIFIC_INITIALIZE_LOADED")) {
+ fpath = mf->GetModulesFile("CMakeSystemSpecificInitialize.cmake");
+ if (!mf->ReadListFile(fpath)) {
+ cmSystemTools::Error("Could not find cmake module file: "
+ "CMakeSystemSpecificInitialize.cmake");
+ }
+ }
+
+ std::map<std::string, bool> needTestLanguage;
+ std::map<std::string, bool> needSetLanguageEnabledMaps;
+ // foreach language
+ // load the CMakeDetermine(LANG)Compiler.cmake file to find
+ // the compiler
+
+ for (std::string const& lang : languages) {
+ needSetLanguageEnabledMaps[lang] = false;
+ if (lang == "NONE") {
+ this->SetLanguageEnabled("NONE", mf);
+ continue;
+ }
+ std::string loadedLang = cmStrCat("CMAKE_", lang, "_COMPILER_LOADED");
+ if (!mf->GetDefinition(loadedLang)) {
+ fpath = cmStrCat(rootBin, "/CMake", lang, "Compiler.cmake");
+
+ // If the existing build tree was already configured with this
+ // version of CMake then try to load the configured file first
+ // to avoid duplicate compiler tests.
+ if (cmSystemTools::FileExists(fpath)) {
+ if (!mf->ReadListFile(fpath)) {
+ cmSystemTools::Error("Could not find cmake module file: " + fpath);
+ }
+ // if this file was found then the language was already determined
+ // to be working
+ needTestLanguage[lang] = false;
+ this->SetLanguageEnabledFlag(lang, mf);
+ needSetLanguageEnabledMaps[lang] = true;
+ // this can only be called after loading CMake(LANG)Compiler.cmake
+ }
+ }
+
+ if (!this->GetLanguageEnabled(lang)) {
+ if (this->CMakeInstance->GetIsInTryCompile()) {
+ cmSystemTools::Error("This should not have happened. "
+ "If you see this message, you are probably "
+ "using a broken CMakeLists.txt file or a "
+ "problematic release of CMake");
+ }
+ // if the CMake(LANG)Compiler.cmake file was not found then
+ // load CMakeDetermine(LANG)Compiler.cmake
+ std::string determineCompiler =
+ cmStrCat("CMakeDetermine", lang, "Compiler.cmake");
+ std::string determineFile = mf->GetModulesFile(determineCompiler);
+ if (!mf->ReadListFile(determineFile)) {
+ cmSystemTools::Error("Could not find cmake module file: " +
+ determineCompiler);
+ }
+ if (cmSystemTools::GetFatalErrorOccured()) {
+ return;
+ }
+ needTestLanguage[lang] = true;
+ // Some generators like visual studio should not use the env variables
+ // So the global generator can specify that in this variable
+ if (!mf->GetDefinition("CMAKE_GENERATOR_NO_COMPILER_ENV")) {
+ // put ${CMake_(LANG)_COMPILER_ENV_VAR}=${CMAKE_(LANG)_COMPILER
+ // into the environment, in case user scripts want to run
+ // configure, or sub cmakes
+ std::string compilerName = cmStrCat("CMAKE_", lang, "_COMPILER");
+ std::string compilerEnv =
+ cmStrCat("CMAKE_", lang, "_COMPILER_ENV_VAR");
+ const std::string& envVar = mf->GetRequiredDefinition(compilerEnv);
+ const std::string& envVarValue =
+ mf->GetRequiredDefinition(compilerName);
+ std::string env = cmStrCat(envVar, '=', envVarValue);
+ cmSystemTools::PutEnv(env);
+ }
+
+ // if determineLanguage was called then load the file it
+ // configures CMake(LANG)Compiler.cmake
+ fpath = cmStrCat(rootBin, "/CMake", lang, "Compiler.cmake");
+ if (!mf->ReadListFile(fpath)) {
+ cmSystemTools::Error("Could not find cmake module file: " + fpath);
+ }
+ this->SetLanguageEnabledFlag(lang, mf);
+ needSetLanguageEnabledMaps[lang] = true;
+ // this can only be called after loading CMake(LANG)Compiler.cmake
+ // the language must be enabled for try compile to work, but we do
+ // not know if it is a working compiler yet so set the test language
+ // flag
+ needTestLanguage[lang] = true;
+ } // end if(!this->GetLanguageEnabled(lang) )
+ } // end loop over languages
+
+ // **** Load the system specific information if not yet loaded
+ if (!mf->GetDefinition("CMAKE_SYSTEM_SPECIFIC_INFORMATION_LOADED")) {
+ fpath = mf->GetModulesFile("CMakeSystemSpecificInformation.cmake");
+ if (!mf->ReadListFile(fpath)) {
+ cmSystemTools::Error("Could not find cmake module file: "
+ "CMakeSystemSpecificInformation.cmake");
+ }
+ }
+ // loop over languages again loading CMake(LANG)Information.cmake
+ //
+ for (std::string const& lang : languages) {
+ if (lang == "NONE") {
+ this->SetLanguageEnabled("NONE", mf);
+ continue;
+ }
+
+ // Check that the compiler was found.
+ std::string compilerName = cmStrCat("CMAKE_", lang, "_COMPILER");
+ std::string compilerEnv = cmStrCat("CMAKE_", lang, "_COMPILER_ENV_VAR");
+ std::ostringstream noCompiler;
+ const char* compilerFile = mf->GetDefinition(compilerName);
+ if (!compilerFile || !*compilerFile || cmIsNOTFOUND(compilerFile)) {
+ /* clang-format off */
+ noCompiler <<
+ "No " << compilerName << " could be found.\n"
+ ;
+ /* clang-format on */
+ } else if ((lang != "RC") && (lang != "ASM_MASM")) {
+ if (!cmSystemTools::FileIsFullPath(compilerFile)) {
+ /* clang-format off */
+ noCompiler <<
+ "The " << compilerName << ":\n"
+ " " << compilerFile << "\n"
+ "is not a full path and was not found in the PATH.\n"
+ ;
+ /* clang-format on */
+ } else if (!cmSystemTools::FileExists(compilerFile)) {
+ /* clang-format off */
+ noCompiler <<
+ "The " << compilerName << ":\n"
+ " " << compilerFile << "\n"
+ "is not a full path to an existing compiler tool.\n"
+ ;
+ /* clang-format on */
+ }
+ }
+ if (!noCompiler.str().empty()) {
+ // Skip testing this language since the compiler is not found.
+ needTestLanguage[lang] = false;
+ if (!optional) {
+ // The compiler was not found and it is not optional. Remove
+ // CMake(LANG)Compiler.cmake so we try again next time CMake runs.
+ std::string compilerLangFile =
+ cmStrCat(rootBin, "/CMake", lang, "Compiler.cmake");
+ cmSystemTools::RemoveFile(compilerLangFile);
+ if (!this->CMakeInstance->GetIsInTryCompile()) {
+ this->PrintCompilerAdvice(noCompiler, lang,
+ mf->GetDefinition(compilerEnv));
+ mf->IssueMessage(MessageType::FATAL_ERROR, noCompiler.str());
+ fatalError = true;
+ }
+ }
+ }
+
+ std::string langLoadedVar =
+ cmStrCat("CMAKE_", lang, "_INFORMATION_LOADED");
+ if (!mf->GetDefinition(langLoadedVar)) {
+ fpath = cmStrCat("CMake", lang, "Information.cmake");
+ std::string informationFile = mf->GetModulesFile(fpath);
+ if (informationFile.empty()) {
+ cmSystemTools::Error("Could not find cmake module file: " + fpath);
+ } else if (!mf->ReadListFile(informationFile)) {
+ cmSystemTools::Error("Could not process cmake module file: " +
+ informationFile);
+ }
+ }
+ if (needSetLanguageEnabledMaps[lang]) {
+ this->SetLanguageEnabledMaps(lang, mf);
+ }
+ this->LanguagesReady.insert(lang);
+
+ // Test the compiler for the language just setup
+ // (but only if a compiler has been actually found)
+ // At this point we should have enough info for a try compile
+ // which is used in the backward stuff
+ // If the language is untested then test it now with a try compile.
+ if (needTestLanguage[lang]) {
+ if (!this->CMakeInstance->GetIsInTryCompile()) {
+ std::string testLang = cmStrCat("CMakeTest", lang, "Compiler.cmake");
+ std::string ifpath = mf->GetModulesFile(testLang);
+ if (!mf->ReadListFile(ifpath)) {
+ cmSystemTools::Error("Could not find cmake module file: " +
+ testLang);
+ }
+ std::string compilerWorks =
+ cmStrCat("CMAKE_", lang, "_COMPILER_WORKS");
+ // if the compiler did not work, then remove the
+ // CMake(LANG)Compiler.cmake file so that it will get tested the
+ // next time cmake is run
+ if (!mf->IsOn(compilerWorks)) {
+ std::string compilerLangFile =
+ cmStrCat(rootBin, "/CMake", lang, "Compiler.cmake");
+ cmSystemTools::RemoveFile(compilerLangFile);
+ }
+ } // end if in try compile
+ } // end need test language
+ // Store the shared library flags so that we can satisfy CMP0018
+ std::string sharedLibFlagsVar =
+ cmStrCat("CMAKE_SHARED_LIBRARY_", lang, "_FLAGS");
+ this->LanguageToOriginalSharedLibFlags[lang] =
+ mf->GetSafeDefinition(sharedLibFlagsVar);
+
+ // Translate compiler ids for compatibility.
+ this->CheckCompilerIdCompatibility(mf, lang);
+ } // end for each language
+
+ // Now load files that can override any settings on the platform or for
+ // the project First load the project compatibility file if it is in
+ // cmake
+ std::string projectCompatibility =
+ cmStrCat(cmSystemTools::GetCMakeRoot(), "/Modules/",
+ mf->GetSafeDefinition("PROJECT_NAME"), "Compatibility.cmake");
+ if (cmSystemTools::FileExists(projectCompatibility)) {
+ mf->ReadListFile(projectCompatibility);
+ }
+ // Inform any extra generator of the new language.
+ if (this->ExtraGenerator) {
+ this->ExtraGenerator->EnableLanguage(languages, mf, false);
+ }
+
+ if (fatalError) {
+ cmSystemTools::SetFatalErrorOccured();
+ }
+
+ for (std::string const& lang : cur_languages) {
+ this->LanguagesInProgress.erase(lang);
+ }
+}
+
+void cmGlobalGenerator::PrintCompilerAdvice(std::ostream& os,
+ std::string const& lang,
+ const char* envVar) const
+{
+ // Subclasses override this method if they do not support this advice.
+ os << "Tell CMake where to find the compiler by setting ";
+ if (envVar) {
+ os << "either the environment variable \"" << envVar << "\" or ";
+ }
+ os << "the CMake cache entry CMAKE_" << lang
+ << "_COMPILER "
+ "to the full path to the compiler, or to the compiler name "
+ "if it is in the PATH.";
+}
+
+void cmGlobalGenerator::CheckCompilerIdCompatibility(
+ cmMakefile* mf, std::string const& lang) const
+{
+ std::string compilerIdVar = "CMAKE_" + lang + "_COMPILER_ID";
+ std::string const compilerId = mf->GetSafeDefinition(compilerIdVar);
+
+ if (compilerId == "AppleClang") {
+ switch (mf->GetPolicyStatus(cmPolicies::CMP0025)) {
+ case cmPolicies::WARN:
+ if (!this->CMakeInstance->GetIsInTryCompile() &&
+ mf->PolicyOptionalWarningEnabled("CMAKE_POLICY_WARNING_CMP0025")) {
+ std::ostringstream w;
+ /* clang-format off */
+ w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0025) << "\n"
+ "Converting " << lang <<
+ R"( compiler id "AppleClang" to "Clang" for compatibility.)"
+ ;
+ /* clang-format on */
+ mf->IssueMessage(MessageType::AUTHOR_WARNING, w.str());
+ }
+ CM_FALLTHROUGH;
+ case cmPolicies::OLD:
+ // OLD behavior is to convert AppleClang to Clang.
+ mf->AddDefinition(compilerIdVar, "Clang");
+ break;
+ case cmPolicies::REQUIRED_IF_USED:
+ case cmPolicies::REQUIRED_ALWAYS:
+ mf->IssueMessage(
+ MessageType::FATAL_ERROR,
+ cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0025));
+ case cmPolicies::NEW:
+ // NEW behavior is to keep AppleClang.
+ break;
+ }
+ }
+
+ if (compilerId == "QCC") {
+ switch (mf->GetPolicyStatus(cmPolicies::CMP0047)) {
+ case cmPolicies::WARN:
+ if (!this->CMakeInstance->GetIsInTryCompile() &&
+ mf->PolicyOptionalWarningEnabled("CMAKE_POLICY_WARNING_CMP0047")) {
+ std::ostringstream w;
+ /* clang-format off */
+ w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0047) << "\n"
+ "Converting " << lang <<
+ R"( compiler id "QCC" to "GNU" for compatibility.)"
+ ;
+ /* clang-format on */
+ mf->IssueMessage(MessageType::AUTHOR_WARNING, w.str());
+ }
+ CM_FALLTHROUGH;
+ case cmPolicies::OLD:
+ // OLD behavior is to convert QCC to GNU.
+ mf->AddDefinition(compilerIdVar, "GNU");
+ if (lang == "C") {
+ mf->AddDefinition("CMAKE_COMPILER_IS_GNUCC", "1");
+ } else if (lang == "CXX") {
+ mf->AddDefinition("CMAKE_COMPILER_IS_GNUCXX", "1");
+ }
+ break;
+ case cmPolicies::REQUIRED_IF_USED:
+ case cmPolicies::REQUIRED_ALWAYS:
+ mf->IssueMessage(
+ MessageType::FATAL_ERROR,
+ cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0047));
+ CM_FALLTHROUGH;
+ case cmPolicies::NEW:
+ // NEW behavior is to keep QCC.
+ break;
+ }
+ }
+
+ if (compilerId == "XLClang") {
+ switch (mf->GetPolicyStatus(cmPolicies::CMP0089)) {
+ case cmPolicies::WARN:
+ if (!this->CMakeInstance->GetIsInTryCompile() &&
+ mf->PolicyOptionalWarningEnabled("CMAKE_POLICY_WARNING_CMP0089")) {
+ std::ostringstream w;
+ /* clang-format off */
+ w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0089) << "\n"
+ "Converting " << lang <<
+ R"( compiler id "XLClang" to "XL" for compatibility.)"
+ ;
+ /* clang-format on */
+ mf->IssueMessage(MessageType::AUTHOR_WARNING, w.str());
+ }
+ CM_FALLTHROUGH;
+ case cmPolicies::OLD:
+ // OLD behavior is to convert XLClang to XL.
+ mf->AddDefinition(compilerIdVar, "XL");
+ break;
+ case cmPolicies::REQUIRED_IF_USED:
+ case cmPolicies::REQUIRED_ALWAYS:
+ mf->IssueMessage(
+ MessageType::FATAL_ERROR,
+ cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0089));
+ case cmPolicies::NEW:
+ // NEW behavior is to keep AppleClang.
+ break;
+ }
+ }
+}
+
+std::string cmGlobalGenerator::GetLanguageOutputExtension(
+ cmSourceFile const& source) const
+{
+ const std::string& lang = source.GetLanguage();
+ if (!lang.empty()) {
+ auto const it = this->LanguageToOutputExtension.find(lang);
+ if (it != this->LanguageToOutputExtension.end()) {
+ return it->second;
+ }
+ } else {
+ // if no language is found then check to see if it is already an
+ // output extension for some language. In that case it should be ignored
+ // and in this map, so it will not be compiled but will just be used.
+ std::string const& ext = source.GetExtension();
+ if (!ext.empty()) {
+ if (this->OutputExtensions.count(ext)) {
+ return ext;
+ }
+ }
+ }
+ return "";
+}
+
+std::string cmGlobalGenerator::GetLanguageFromExtension(const char* ext) const
+{
+ // if there is an extension and it starts with . then move past the
+ // . because the extensions are not stored with a . in the map
+ if (ext && *ext == '.') {
+ ++ext;
+ }
+ auto const it = this->ExtensionToLanguage.find(ext);
+ if (it != this->ExtensionToLanguage.end()) {
+ return it->second;
+ }
+ return "";
+}
+
+/* SetLanguageEnabled() is now split in two parts:
+at first the enabled-flag is set. This can then be used in EnabledLanguage()
+for checking whether the language is already enabled. After setting this
+flag still the values from the cmake variables have to be copied into the
+internal maps, this is done in SetLanguageEnabledMaps() which is called
+after the system- and compiler specific files have been loaded.
+
+This split was done originally so that compiler-specific configuration
+files could change the object file extension
+(CMAKE_<LANG>_OUTPUT_EXTENSION) before the CMake variables were copied
+to the C++ maps.
+*/
+void cmGlobalGenerator::SetLanguageEnabled(const std::string& l,
+ cmMakefile* mf)
+{
+ this->SetLanguageEnabledFlag(l, mf);
+ this->SetLanguageEnabledMaps(l, mf);
+}
+
+void cmGlobalGenerator::SetLanguageEnabledFlag(const std::string& l,
+ cmMakefile* mf)
+{
+ this->CMakeInstance->GetState()->SetLanguageEnabled(l);
+
+ // Fill the language-to-extension map with the current variable
+ // settings to make sure it is available for the try_compile()
+ // command source file signature. In SetLanguageEnabledMaps this
+ // will be done again to account for any compiler- or
+ // platform-specific entries.
+ this->FillExtensionToLanguageMap(l, mf);
+}
+
+void cmGlobalGenerator::SetLanguageEnabledMaps(const std::string& l,
+ cmMakefile* mf)
+{
+ // use LanguageToLinkerPreference to detect whether this functions has
+ // run before
+ if (cmContains(this->LanguageToLinkerPreference, l)) {
+ return;
+ }
+
+ std::string linkerPrefVar =
+ std::string("CMAKE_") + std::string(l) + std::string("_LINKER_PREFERENCE");
+ const char* linkerPref = mf->GetDefinition(linkerPrefVar);
+ int preference = 0;
+ if (linkerPref) {
+ if (sscanf(linkerPref, "%d", &preference) != 1) {
+ // backward compatibility: before 2.6 LINKER_PREFERENCE
+ // was either "None" or "Preferred", and only the first character was
+ // tested. So if there is a custom language out there and it is
+ // "Preferred", set its preference high
+ if (linkerPref[0] == 'P') {
+ preference = 100;
+ } else {
+ preference = 0;
+ }
+ }
+ }
+
+ if (preference < 0) {
+ std::string msg =
+ cmStrCat(linkerPrefVar, " is negative, adjusting it to 0");
+ cmSystemTools::Message(msg, "Warning");
+ preference = 0;
+ }
+
+ this->LanguageToLinkerPreference[l] = preference;
+
+ std::string outputExtensionVar =
+ std::string("CMAKE_") + std::string(l) + std::string("_OUTPUT_EXTENSION");
+ const char* outputExtension = mf->GetDefinition(outputExtensionVar);
+ if (outputExtension) {
+ this->LanguageToOutputExtension[l] = outputExtension;
+ this->OutputExtensions[outputExtension] = outputExtension;
+ if (outputExtension[0] == '.') {
+ this->OutputExtensions[outputExtension + 1] = outputExtension + 1;
+ }
+ }
+
+ // The map was originally filled by SetLanguageEnabledFlag, but
+ // since then the compiler- and platform-specific files have been
+ // loaded which might have added more entries.
+ this->FillExtensionToLanguageMap(l, mf);
+
+ std::string ignoreExtensionsVar =
+ std::string("CMAKE_") + std::string(l) + std::string("_IGNORE_EXTENSIONS");
+ std::string ignoreExts = mf->GetSafeDefinition(ignoreExtensionsVar);
+ std::vector<std::string> extensionList = cmExpandedList(ignoreExts);
+ for (std::string const& i : extensionList) {
+ this->IgnoreExtensions[i] = true;
+ }
+}
+
+void cmGlobalGenerator::FillExtensionToLanguageMap(const std::string& l,
+ cmMakefile* mf)
+{
+ std::string extensionsVar = std::string("CMAKE_") + std::string(l) +
+ std::string("_SOURCE_FILE_EXTENSIONS");
+ const std::string& exts = mf->GetSafeDefinition(extensionsVar);
+ std::vector<std::string> extensionList = cmExpandedList(exts);
+ for (std::string const& i : extensionList) {
+ this->ExtensionToLanguage[i] = l;
+ }
+}
+
+const char* cmGlobalGenerator::GetGlobalSetting(std::string const& name) const
+{
+ assert(!this->Makefiles.empty());
+ return this->Makefiles[0]->GetDefinition(name);
+}
+
+bool cmGlobalGenerator::GlobalSettingIsOn(std::string const& name) const
+{
+ assert(!this->Makefiles.empty());
+ return this->Makefiles[0]->IsOn(name);
+}
+
+std::string cmGlobalGenerator::GetSafeGlobalSetting(
+ std::string const& name) const
+{
+ assert(!this->Makefiles.empty());
+ return this->Makefiles[0]->GetSafeDefinition(name);
+}
+
+bool cmGlobalGenerator::IgnoreFile(const char* ext) const
+{
+ if (!this->GetLanguageFromExtension(ext).empty()) {
+ return false;
+ }
+ return (this->IgnoreExtensions.count(ext) > 0);
+}
+
+bool cmGlobalGenerator::GetLanguageEnabled(const std::string& l) const
+{
+ return this->CMakeInstance->GetState()->GetLanguageEnabled(l);
+}
+
+void cmGlobalGenerator::ClearEnabledLanguages()
+{
+ this->CMakeInstance->GetState()->ClearEnabledLanguages();
+}
+
+void cmGlobalGenerator::CreateLocalGenerators()
+{
+ this->LocalGeneratorSearchIndex.clear();
+ this->LocalGenerators.clear();
+ this->LocalGenerators.reserve(this->Makefiles.size());
+ for (const auto& m : this->Makefiles) {
+ auto lg = this->CreateLocalGenerator(m.get());
+ this->IndexLocalGenerator(lg.get());
+ this->LocalGenerators.push_back(std::move(lg));
+ }
+}
+
+void cmGlobalGenerator::Configure()
+{
+ this->FirstTimeProgress = 0.0f;
+ this->ClearGeneratorMembers();
+
+ cmStateSnapshot snapshot = this->CMakeInstance->GetCurrentSnapshot();
+
+ snapshot.GetDirectory().SetCurrentSource(
+ this->CMakeInstance->GetHomeDirectory());
+ snapshot.GetDirectory().SetCurrentBinary(
+ this->CMakeInstance->GetHomeOutputDirectory());
+
+ auto dirMfu = cm::make_unique<cmMakefile>(this, snapshot);
+ auto dirMf = dirMfu.get();
+ this->Makefiles.push_back(std::move(dirMfu));
+ dirMf->SetRecursionDepth(this->RecursionDepth);
+ this->IndexMakefile(dirMf);
+
+ this->BinaryDirectories.insert(
+ this->CMakeInstance->GetHomeOutputDirectory());
+
+ // now do it
+ this->ConfigureDoneCMP0026AndCMP0024 = false;
+ dirMf->Configure();
+ dirMf->EnforceDirectoryLevelRules();
+
+ this->ConfigureDoneCMP0026AndCMP0024 = true;
+
+ // Put a copy of each global target in every directory.
+ {
+ std::vector<GlobalTargetInfo> globalTargets;
+ this->CreateDefaultGlobalTargets(globalTargets);
+
+ for (const auto& mf : this->Makefiles) {
+ auto& targets = mf->GetTargets();
+ for (GlobalTargetInfo const& globalTarget : globalTargets) {
+ targets.emplace(globalTarget.Name,
+ this->CreateGlobalTarget(globalTarget, mf.get()));
+ }
+ }
+ }
+
+ // update the cache entry for the number of local generators, this is used
+ // for progress
+ char num[100];
+ sprintf(num, "%d", static_cast<int>(this->Makefiles.size()));
+ this->GetCMakeInstance()->AddCacheEntry("CMAKE_NUMBER_OF_MAKEFILES", num,
+ "number of local generators",
+ cmStateEnums::INTERNAL);
+
+ if (this->CMakeInstance->GetWorkingMode() == cmake::NORMAL_MODE) {
+ std::ostringstream msg;
+ if (cmSystemTools::GetErrorOccuredFlag()) {
+ msg << "Configuring incomplete, errors occurred!";
+ const char* logs[] = { "CMakeOutput.log", "CMakeError.log", nullptr };
+ for (const char** log = logs; *log; ++log) {
+ std::string f = cmStrCat(this->CMakeInstance->GetHomeOutputDirectory(),
+ "/CMakeFiles/", *log);
+ if (cmSystemTools::FileExists(f)) {
+ msg << "\nSee also \"" << f << "\".";
+ }
+ }
+ } else {
+ msg << "Configuring done";
+ }
+ this->CMakeInstance->UpdateProgress(msg.str(), -1);
+ }
+}
+
+void cmGlobalGenerator::CreateGenerationObjects(TargetTypes targetTypes)
+{
+ this->CreateLocalGenerators();
+ // Commit side effects only if we are actually generating
+ if (this->GetConfigureDoneCMP0026()) {
+ this->CheckTargetProperties();
+ }
+ this->CreateGeneratorTargets(targetTypes);
+ this->ComputeBuildFileGenerators();
+}
+
+void cmGlobalGenerator::CreateImportedGenerationObjects(
+ cmMakefile* mf, const std::vector<std::string>& targets,
+ std::vector<const cmGeneratorTarget*>& exports)
+{
+ this->CreateGenerationObjects(ImportedOnly);
+ auto const mfit =
+ std::find_if(this->Makefiles.begin(), this->Makefiles.end(),
+ [mf](const std::unique_ptr<cmMakefile>& item) {
+ return item.get() == mf;
+ });
+ auto& lg =
+ this->LocalGenerators[std::distance(this->Makefiles.begin(), mfit)];
+ for (std::string const& t : targets) {
+ cmGeneratorTarget* gt = lg->FindGeneratorTargetToUse(t);
+ if (gt) {
+ exports.push_back(gt);
+ }
+ }
+}
+
+cmExportBuildFileGenerator* cmGlobalGenerator::GetExportedTargetsFile(
+ const std::string& filename) const
+{
+ auto const it = this->BuildExportSets.find(filename);
+ return it == this->BuildExportSets.end() ? nullptr : it->second;
+}
+
+void cmGlobalGenerator::AddCMP0042WarnTarget(const std::string& target)
+{
+ this->CMP0042WarnTargets.insert(target);
+}
+
+void cmGlobalGenerator::AddCMP0068WarnTarget(const std::string& target)
+{
+ this->CMP0068WarnTargets.insert(target);
+}
+
+bool cmGlobalGenerator::CheckALLOW_DUPLICATE_CUSTOM_TARGETS() const
+{
+ // If the property is not enabled then okay.
+ if (!this->CMakeInstance->GetState()->GetGlobalPropertyAsBool(
+ "ALLOW_DUPLICATE_CUSTOM_TARGETS")) {
+ return true;
+ }
+
+ // This generator does not support duplicate custom targets.
+ std::ostringstream e;
+ e << "This project has enabled the ALLOW_DUPLICATE_CUSTOM_TARGETS "
+ << "global property. "
+ << "The \"" << this->GetName() << "\" generator does not support "
+ << "duplicate custom targets. "
+ << "Consider using a Makefiles generator or fix the project to not "
+ << "use duplicate target names.";
+ cmSystemTools::Error(e.str());
+ return false;
+}
+
+void cmGlobalGenerator::ComputeBuildFileGenerators()
+{
+ for (unsigned int i = 0; i < this->LocalGenerators.size(); ++i) {
+ std::vector<std::unique_ptr<cmExportBuildFileGenerator>> const& gens =
+ this->Makefiles[i]->GetExportBuildFileGenerators();
+ for (std::unique_ptr<cmExportBuildFileGenerator> const& g : gens) {
+ g->Compute(this->LocalGenerators[i].get());
+ }
+ }
+}
+
+bool cmGlobalGenerator::UnsupportedVariableIsDefined(const std::string& name,
+ bool supported) const
+{
+ if (!supported && this->Makefiles.front()->GetDefinition(name)) {
+ std::ostringstream e;
+ /* clang-format off */
+ e <<
+ "Generator\n"
+ " " << this->GetName() << "\n"
+ "does not support variable\n"
+ " " << name << "\n"
+ "but it has been specified."
+ ;
+ /* clang-format on */
+ this->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR, e.str());
+ return true;
+ }
+
+ return false;
+}
+
+bool cmGlobalGenerator::Compute()
+{
+ // Make sure unsupported variables are not used.
+ if (this->UnsupportedVariableIsDefined("CMAKE_DEFAULT_BUILD_TYPE",
+ this->SupportsDefaultBuildType())) {
+ return false;
+ }
+ if (this->UnsupportedVariableIsDefined("CMAKE_CROSS_CONFIGS",
+ this->SupportsCrossConfigs())) {
+ return false;
+ }
+ if (this->UnsupportedVariableIsDefined("CMAKE_DEFAULT_CONFIGS",
+ this->SupportsDefaultConfigs())) {
+ return false;
+ }
+
+ // Some generators track files replaced during the Generate.
+ // Start with an empty vector:
+ this->FilesReplacedDuringGenerate.clear();
+
+ // clear targets to issue warning CMP0042 for
+ this->CMP0042WarnTargets.clear();
+ // clear targets to issue warning CMP0068 for
+ this->CMP0068WarnTargets.clear();
+
+ // Check whether this generator is allowed to run.
+ if (!this->CheckALLOW_DUPLICATE_CUSTOM_TARGETS()) {
+ return false;
+ }
+ this->FinalizeTargetCompileInfo();
+
+ this->CreateGenerationObjects();
+
+ // at this point this->LocalGenerators has been filled,
+ // so create the map from project name to vector of local generators
+ this->FillProjectMap();
+
+ // Iterate through all targets and set up AUTOMOC, AUTOUIC and AUTORCC
+ if (!this->QtAutoGen()) {
+ return false;
+ }
+
+ // Add automatically generated sources (e.g. unity build).
+ if (!this->AddAutomaticSources()) {
+ return false;
+ }
+
+ // Add generator specific helper commands
+ for (const auto& localGen : this->LocalGenerators) {
+ localGen->AddHelperCommands();
+ }
+
+ // Finalize the set of compile features for each target.
+ // FIXME: This turns into calls to cmMakefile::AddRequiredTargetFeature
+ // which actually modifies the <lang>_STANDARD target property
+ // on the original cmTarget instance. It accumulates features
+ // across all configurations. Some refactoring is needed to
+ // compute a per-config resulta purely during generation.
+ for (const auto& localGen : this->LocalGenerators) {
+ if (!localGen->ComputeTargetCompileFeatures()) {
+ return false;
+ }
+ }
+
+ for (const auto& localGen : this->LocalGenerators) {
+ cmMakefile* mf = localGen->GetMakefile();
+ for (const auto& g : mf->GetInstallGenerators()) {
+ if (!g->Compute(localGen.get())) {
+ return false;
+ }
+ }
+ }
+
+ this->AddExtraIDETargets();
+
+ // Trace the dependencies, after that no custom commands should be added
+ // because their dependencies might not be handled correctly
+ for (const auto& localGen : this->LocalGenerators) {
+ localGen->TraceDependencies();
+ }
+
+ // Make sure that all (non-imported) targets have source files added!
+ if (this->CheckTargetsForMissingSources()) {
+ return false;
+ }
+
+ this->ForceLinkerLanguages();
+
+ // Compute the manifest of main targets generated.
+ for (const auto& localGen : this->LocalGenerators) {
+ localGen->ComputeTargetManifest();
+ }
+
+ // Compute the inter-target dependencies.
+ if (!this->ComputeTargetDepends()) {
+ return false;
+ }
+
+ if (this->CheckTargetsForType()) {
+ return false;
+ }
+
+ if (this->CheckTargetsForPchCompilePdb()) {
+ return false;
+ }
+
+ for (const auto& localGen : this->LocalGenerators) {
+ localGen->ComputeHomeRelativeOutputPath();
+ }
+
+ return true;
+}
+
+void cmGlobalGenerator::Generate()
+{
+ // Create a map from local generator to the complete set of targets
+ // it builds by default.
+ this->InitializeProgressMarks();
+
+ this->ProcessEvaluationFiles();
+
+ this->CMakeInstance->UpdateProgress("Generating", 0.1f);
+
+ // Generate project files
+ for (unsigned int i = 0; i < this->LocalGenerators.size(); ++i) {
+ this->SetCurrentMakefile(this->LocalGenerators[i]->GetMakefile());
+ this->LocalGenerators[i]->Generate();
+ if (!this->LocalGenerators[i]->GetMakefile()->IsOn(
+ "CMAKE_SKIP_INSTALL_RULES")) {
+ this->LocalGenerators[i]->GenerateInstallRules();
+ }
+ this->LocalGenerators[i]->GenerateTestFiles();
+ this->CMakeInstance->UpdateProgress(
+ "Generating",
+ 0.1f +
+ 0.9f * (static_cast<float>(i) + 1.0f) /
+ static_cast<float>(this->LocalGenerators.size()));
+ }
+ this->SetCurrentMakefile(nullptr);
+
+ if (!this->GenerateCPackPropertiesFile()) {
+ this->GetCMakeInstance()->IssueMessage(
+ MessageType::FATAL_ERROR, "Could not write CPack properties file.");
+ }
+
+ for (auto& buildExpSet : this->BuildExportSets) {
+ if (!buildExpSet.second->GenerateImportFile()) {
+ if (!cmSystemTools::GetErrorOccuredFlag()) {
+ this->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR,
+ "Could not write export file.");
+ }
+ return;
+ }
+ }
+ // Update rule hashes.
+ this->CheckRuleHashes();
+
+ this->WriteSummary();
+
+ if (this->ExtraGenerator) {
+ this->ExtraGenerator->Generate();
+ }
+
+ if (!this->CMP0042WarnTargets.empty()) {
+ std::ostringstream w;
+ w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0042) << "\n";
+ w << "MACOSX_RPATH is not specified for"
+ " the following targets:\n";
+ for (std::string const& t : this->CMP0042WarnTargets) {
+ w << " " << t << "\n";
+ }
+ this->GetCMakeInstance()->IssueMessage(MessageType::AUTHOR_WARNING,
+ w.str());
+ }
+
+ if (!this->CMP0068WarnTargets.empty()) {
+ std::ostringstream w;
+ /* clang-format off */
+ w <<
+ cmPolicies::GetPolicyWarning(cmPolicies::CMP0068) << "\n"
+ "For compatibility with older versions of CMake, the install_name "
+ "fields for the following targets are still affected by RPATH "
+ "settings:\n"
+ ;
+ /* clang-format on */
+ for (std::string const& t : this->CMP0068WarnTargets) {
+ w << " " << t << "\n";
+ }
+ this->GetCMakeInstance()->IssueMessage(MessageType::AUTHOR_WARNING,
+ w.str());
+ }
+
+ this->CMakeInstance->UpdateProgress("Generating done", -1);
+}
+
+bool cmGlobalGenerator::ComputeTargetDepends()
+{
+ cmComputeTargetDepends ctd(this);
+ if (!ctd.Compute()) {
+ return false;
+ }
+ for (cmGeneratorTarget const* target : ctd.GetTargets()) {
+ ctd.GetTargetDirectDepends(target, this->TargetDependencies[target]);
+ }
+ return true;
+}
+
+bool cmGlobalGenerator::QtAutoGen()
+{
+#ifndef CMAKE_BOOTSTRAP
+ cmQtAutoGenGlobalInitializer initializer(this->LocalGenerators);
+ return initializer.generate();
+#else
+ return true;
+#endif
+}
+
+bool cmGlobalGenerator::AddAutomaticSources()
+{
+ for (const auto& lg : this->LocalGenerators) {
+ lg->CreateEvaluationFileOutputs();
+ for (const auto& gt : lg->GetGeneratorTargets()) {
+ if (gt->GetType() == cmStateEnums::INTERFACE_LIBRARY ||
+ gt->GetType() == cmStateEnums::UTILITY ||
+ gt->GetType() == cmStateEnums::GLOBAL_TARGET) {
+ continue;
+ }
+ lg->AddUnityBuild(gt.get());
+ lg->AddPchDependencies(gt.get());
+ }
+ }
+ // The above transformations may have changed the classification of sources.
+ // Clear the source list and classification cache (KindedSources) of all
+ // targets so that it will be recomputed correctly by the generators later
+ // now that the above transformations are done for all targets.
+ for (const auto& lg : this->LocalGenerators) {
+ for (const auto& gt : lg->GetGeneratorTargets()) {
+ gt->ClearSourcesCache();
+ }
+ }
+ return true;
+}
+
+std::unique_ptr<cmLinkLineComputer> cmGlobalGenerator::CreateLinkLineComputer(
+ cmOutputConverter* outputConverter, cmStateDirectory const& stateDir) const
+{
+ return cm::make_unique<cmLinkLineComputer>(outputConverter, stateDir);
+}
+
+std::unique_ptr<cmLinkLineComputer>
+cmGlobalGenerator::CreateMSVC60LinkLineComputer(
+ cmOutputConverter* outputConverter, cmStateDirectory const& stateDir) const
+{
+ return std::unique_ptr<cmLinkLineComputer>(
+ cm::make_unique<cmMSVC60LinkLineComputer>(outputConverter, stateDir));
+}
+
+void cmGlobalGenerator::FinalizeTargetCompileInfo()
+{
+ std::vector<std::string> const langs =
+ this->CMakeInstance->GetState()->GetEnabledLanguages();
+
+ // Construct per-target generator information.
+ for (const auto& mf : this->Makefiles) {
+ const cmStringRange noconfig_compile_definitions =
+ mf->GetCompileDefinitionsEntries();
+ const cmBacktraceRange noconfig_compile_definitions_bts =
+ mf->GetCompileDefinitionsBacktraces();
+
+ for (auto& target : mf->GetTargets()) {
+ cmTarget* t = &target.second;
+ if (t->GetType() == cmStateEnums::GLOBAL_TARGET) {
+ continue;
+ }
+
+ t->AppendBuildInterfaceIncludes();
+
+ if (t->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
+ continue;
+ }
+
+ {
+ auto btIt = noconfig_compile_definitions_bts.begin();
+ auto it = noconfig_compile_definitions.begin();
+ for (; it != noconfig_compile_definitions.end(); ++it, ++btIt) {
+ t->InsertCompileDefinition(*it, *btIt);
+ }
+ }
+
+ cmPolicies::PolicyStatus polSt =
+ mf->GetPolicyStatus(cmPolicies::CMP0043);
+ if (polSt == cmPolicies::WARN || polSt == cmPolicies::OLD) {
+ std::vector<std::string> configs;
+ mf->GetConfigurations(configs);
+
+ for (std::string const& c : configs) {
+ std::string defPropName =
+ cmStrCat("COMPILE_DEFINITIONS_", cmSystemTools::UpperCase(c));
+ if (cmProp val = mf->GetProperty(defPropName)) {
+ t->AppendProperty(defPropName, *val);
+ }
+ }
+ }
+ }
+
+ // The standard include directories for each language
+ // should be treated as system include directories.
+ std::set<std::string> standardIncludesSet;
+ for (std::string const& li : langs) {
+ std::string const standardIncludesVar =
+ "CMAKE_" + li + "_STANDARD_INCLUDE_DIRECTORIES";
+ std::string const& standardIncludesStr =
+ mf->GetSafeDefinition(standardIncludesVar);
+ std::vector<std::string> standardIncludesVec =
+ cmExpandedList(standardIncludesStr);
+ standardIncludesSet.insert(standardIncludesVec.begin(),
+ standardIncludesVec.end());
+ }
+ mf->AddSystemIncludeDirectories(standardIncludesSet);
+ }
+}
+
+void cmGlobalGenerator::CreateGeneratorTargets(
+ TargetTypes targetTypes, cmMakefile* mf, cmLocalGenerator* lg,
+ std::map<cmTarget*, cmGeneratorTarget*> const& importedMap)
+{
+ if (targetTypes == AllTargets) {
+ for (auto& target : mf->GetTargets()) {
+ cmTarget* t = &target.second;
+ lg->AddGeneratorTarget(cm::make_unique<cmGeneratorTarget>(t, lg));
+ }
+ }
+
+ for (cmTarget* t : mf->GetImportedTargets()) {
+ lg->AddImportedGeneratorTarget(importedMap.find(t)->second);
+ }
+}
+
+void cmGlobalGenerator::CreateGeneratorTargets(TargetTypes targetTypes)
+{
+ std::map<cmTarget*, cmGeneratorTarget*> importedMap;
+ for (unsigned int i = 0; i < this->Makefiles.size(); ++i) {
+ auto& mf = this->Makefiles[i];
+ for (const auto& ownedImpTgt : mf->GetOwnedImportedTargets()) {
+ cmLocalGenerator* lg = this->LocalGenerators[i].get();
+ auto gt = cm::make_unique<cmGeneratorTarget>(ownedImpTgt.get(), lg);
+ importedMap[ownedImpTgt.get()] = gt.get();
+ lg->AddOwnedImportedGeneratorTarget(std::move(gt));
+ }
+ }
+
+ // Construct per-target generator information.
+ for (unsigned int i = 0; i < this->LocalGenerators.size(); ++i) {
+ this->CreateGeneratorTargets(targetTypes, this->Makefiles[i].get(),
+ this->LocalGenerators[i].get(), importedMap);
+ }
+}
+
+void cmGlobalGenerator::ClearGeneratorMembers()
+{
+ this->BuildExportSets.clear();
+
+ this->Makefiles.clear();
+
+ this->LocalGenerators.clear();
+
+ this->AliasTargets.clear();
+ this->ExportSets.clear();
+ this->TargetDependencies.clear();
+ this->TargetSearchIndex.clear();
+ this->GeneratorTargetSearchIndex.clear();
+ this->MakefileSearchIndex.clear();
+ this->LocalGeneratorSearchIndex.clear();
+ this->ProjectMap.clear();
+ this->RuleHashes.clear();
+ this->DirectoryContentMap.clear();
+ this->BinaryDirectories.clear();
+}
+
+void cmGlobalGenerator::ComputeTargetObjectDirectory(
+ cmGeneratorTarget* /*unused*/) const
+{
+}
+
+void cmGlobalGenerator::CheckTargetProperties()
+{
+ // check for link libraries and include directories containing "NOTFOUND"
+ // and for infinite loops
+ std::map<std::string, std::string> notFoundMap;
+ cmState* state = this->GetCMakeInstance()->GetState();
+ for (unsigned int i = 0; i < this->Makefiles.size(); ++i) {
+ this->Makefiles[i]->Generate(*this->LocalGenerators[i]);
+ for (auto const& target : this->Makefiles[i]->GetTargets()) {
+ if (target.second.GetType() == cmStateEnums::INTERFACE_LIBRARY) {
+ continue;
+ }
+ for (auto const& lib : target.second.GetOriginalLinkLibraries()) {
+ if (lib.first.size() > 9 && cmIsNOTFOUND(lib.first)) {
+ std::string varName = lib.first.substr(0, lib.first.size() - 9);
+ if (state->GetCacheEntryPropertyAsBool(varName, "ADVANCED")) {
+ varName += " (ADVANCED)";
+ }
+ std::string text =
+ cmStrCat(notFoundMap[varName], "\n linked by target \"",
+ target.second.GetName(), "\" in directory ",
+ this->Makefiles[i]->GetCurrentSourceDirectory());
+ notFoundMap[varName] = text;
+ }
+ }
+ std::vector<std::string> incs;
+ cmProp incDirProp = target.second.GetProperty("INCLUDE_DIRECTORIES");
+ if (!incDirProp) {
+ continue;
+ }
+
+ std::string incDirs = cmGeneratorExpression::Preprocess(
+ *incDirProp, cmGeneratorExpression::StripAllGeneratorExpressions);
+
+ cmExpandList(incDirs, incs);
+
+ for (std::string const& incDir : incs) {
+ if (incDir.size() > 9 && cmIsNOTFOUND(incDir)) {
+ std::string varName = incDir.substr(0, incDir.size() - 9);
+ if (state->GetCacheEntryPropertyAsBool(varName, "ADVANCED")) {
+ varName += " (ADVANCED)";
+ }
+ std::string text =
+ cmStrCat(notFoundMap[varName],
+ "\n used as include directory in directory ",
+ this->Makefiles[i]->GetCurrentSourceDirectory());
+ notFoundMap[varName] = text;
+ }
+ }
+ }
+ }
+
+ if (!notFoundMap.empty()) {
+ std::string notFoundVars;
+ for (auto const& notFound : notFoundMap) {
+ notFoundVars += notFound.first;
+ notFoundVars += notFound.second;
+ notFoundVars += "\n";
+ }
+ cmSystemTools::Error("The following variables are used in this project, "
+ "but they are set to NOTFOUND.\n"
+ "Please set them or make sure they are set and "
+ "tested correctly in the CMake files:\n" +
+ notFoundVars);
+ }
+}
+
+int cmGlobalGenerator::TryCompile(int jobs, const std::string& srcdir,
+ const std::string& bindir,
+ const std::string& projectName,
+ const std::string& target, bool fast,
+ std::string& output, cmMakefile* mf)
+{
+ // if this is not set, then this is a first time configure
+ // and there is a good chance that the try compile stuff will
+ // take the bulk of the time, so try and guess some progress
+ // by getting closer and closer to 100 without actually getting there.
+ if (!this->CMakeInstance->GetState()->GetInitializedCacheValue(
+ "CMAKE_NUMBER_OF_MAKEFILES")) {
+ // If CMAKE_NUMBER_OF_MAKEFILES is not set
+ // we are in the first time progress and we have no
+ // idea how long it will be. So, just move 1/10th of the way
+ // there each time, and don't go over 95%
+ this->FirstTimeProgress += ((1.0f - this->FirstTimeProgress) / 30.0f);
+ if (this->FirstTimeProgress > 0.95f) {
+ this->FirstTimeProgress = 0.95f;
+ }
+ this->CMakeInstance->UpdateProgress("Configuring",
+ this->FirstTimeProgress);
+ }
+
+ std::vector<std::string> newTarget = {};
+ if (!target.empty()) {
+ newTarget = { target };
+ }
+ std::string config =
+ mf->GetSafeDefinition("CMAKE_TRY_COMPILE_CONFIGURATION");
+ return this->Build(jobs, srcdir, bindir, projectName, newTarget, output, "",
+ config, false, fast, false, this->TryCompileTimeout);
+}
+
+std::vector<cmGlobalGenerator::GeneratedMakeCommand>
+cmGlobalGenerator::GenerateBuildCommand(
+ const std::string& /*unused*/, const std::string& /*unused*/,
+ const std::string& /*unused*/, std::vector<std::string> const& /*unused*/,
+ const std::string& /*unused*/, bool /*unused*/, int /*unused*/,
+ bool /*unused*/, std::vector<std::string> const& /*unused*/)
+{
+ GeneratedMakeCommand makeCommand;
+ makeCommand.Add("cmGlobalGenerator::GenerateBuildCommand not implemented");
+ return { std::move(makeCommand) };
+}
+
+void cmGlobalGenerator::PrintBuildCommandAdvice(std::ostream& /*os*/,
+ int /*jobs*/) const
+{
+ // Subclasses override this method if they e.g want to give a warning that
+ // they do not support certain build command line options
+}
+
+int cmGlobalGenerator::Build(
+ int jobs, const std::string& /*unused*/, const std::string& bindir,
+ const std::string& projectName, const std::vector<std::string>& targets,
+ std::string& output, const std::string& makeCommandCSTR,
+ const std::string& config, bool clean, bool fast, bool verbose,
+ cmDuration timeout, cmSystemTools::OutputOption outputflag,
+ std::vector<std::string> const& nativeOptions)
+{
+ bool hideconsole = cmSystemTools::GetRunCommandHideConsole();
+
+ /**
+ * Run an executable command and put the stdout in output.
+ */
+ cmWorkingDirectory workdir(bindir);
+ output += "Change Dir: ";
+ output += bindir;
+ output += "\n";
+ if (workdir.Failed()) {
+ cmSystemTools::SetRunCommandHideConsole(hideconsole);
+ std::string err = cmStrCat("Failed to change directory: ",
+ std::strerror(workdir.GetLastResult()));
+ cmSystemTools::Error(err);
+ output += err;
+ output += "\n";
+ return 1;
+ }
+ std::string realConfig = config;
+ if (realConfig.empty()) {
+ realConfig = this->GetDefaultBuildConfig();
+ }
+
+ int retVal = 0;
+ cmSystemTools::SetRunCommandHideConsole(true);
+ std::string outputBuffer;
+ std::string* outputPtr = &outputBuffer;
+
+ std::vector<GeneratedMakeCommand> makeCommand =
+ this->GenerateBuildCommand(makeCommandCSTR, projectName, bindir, targets,
+ realConfig, fast, jobs, verbose, nativeOptions);
+
+ // Workaround to convince some commands to produce output.
+ if (outputflag == cmSystemTools::OUTPUT_PASSTHROUGH &&
+ makeCommand.back().RequiresOutputForward) {
+ outputflag = cmSystemTools::OUTPUT_FORWARD;
+ }
+
+ // should we do a clean first?
+ if (clean) {
+ std::vector<GeneratedMakeCommand> cleanCommand =
+ this->GenerateBuildCommand(makeCommandCSTR, projectName, bindir,
+ { "clean" }, realConfig, fast, jobs, verbose);
+ output += "\nRun Clean Command:";
+ output += cleanCommand.front().Printable();
+ output += "\n";
+ if (cleanCommand.size() != 1) {
+ this->GetCMakeInstance()->IssueMessage(MessageType::INTERNAL_ERROR,
+ "The generator did not produce "
+ "exactly one command for the "
+ "'clean' target");
+ return 1;
+ }
+ if (!cmSystemTools::RunSingleCommand(cleanCommand.front().PrimaryCommand,
+ outputPtr, outputPtr, &retVal,
+ nullptr, outputflag, timeout)) {
+ cmSystemTools::SetRunCommandHideConsole(hideconsole);
+ cmSystemTools::Error("Generator: execution of make clean failed.");
+ output += *outputPtr;
+ output += "\nGenerator: execution of make clean failed.\n";
+
+ return 1;
+ }
+ output += *outputPtr;
+ }
+
+ // now build
+ std::string makeCommandStr;
+ output += "\nRun Build Command(s):";
+
+ for (auto command = makeCommand.begin(); command != makeCommand.end();
+ ++command) {
+ makeCommandStr = command->Printable();
+ if (command != makeCommand.end()) {
+ makeCommandStr += " && ";
+ }
+
+ output += makeCommandStr;
+ if (!cmSystemTools::RunSingleCommand(command->PrimaryCommand, outputPtr,
+ outputPtr, &retVal, nullptr,
+ outputflag, timeout)) {
+ cmSystemTools::SetRunCommandHideConsole(hideconsole);
+ cmSystemTools::Error(
+ "Generator: execution of make failed. Make command was: " +
+ makeCommandStr);
+ output += *outputPtr;
+ output += "\nGenerator: execution of make failed. Make command was: " +
+ makeCommandStr + "\n";
+
+ return 1;
+ }
+ output += *outputPtr;
+ }
+ output += "\n";
+ cmSystemTools::SetRunCommandHideConsole(hideconsole);
+
+ // The OpenWatcom tools do not return an error code when a link
+ // library is not found!
+ if (this->CMakeInstance->GetState()->UseWatcomWMake() && retVal == 0 &&
+ output.find("W1008: cannot open") != std::string::npos) {
+ retVal = 1;
+ }
+
+ return retVal;
+}
+
+bool cmGlobalGenerator::Open(const std::string& bindir,
+ const std::string& projectName, bool dryRun)
+{
+ if (this->ExtraGenerator) {
+ return this->ExtraGenerator->Open(bindir, projectName, dryRun);
+ }
+
+ return false;
+}
+
+std::string cmGlobalGenerator::GenerateCMakeBuildCommand(
+ const std::string& target, const std::string& config,
+ const std::string& native, bool ignoreErrors)
+{
+ std::string makeCommand = cmSystemTools::GetCMakeCommand();
+ makeCommand =
+ cmStrCat(cmSystemTools::ConvertToOutputPath(makeCommand), " --build .");
+ if (!config.empty()) {
+ makeCommand += " --config \"";
+ makeCommand += config;
+ makeCommand += "\"";
+ }
+ if (!target.empty()) {
+ makeCommand += " --target \"";
+ makeCommand += target;
+ makeCommand += "\"";
+ }
+ const char* sep = " -- ";
+ if (ignoreErrors) {
+ const char* iflag = this->GetBuildIgnoreErrorsFlag();
+ if (iflag && *iflag) {
+ makeCommand += sep;
+ makeCommand += iflag;
+ sep = " ";
+ }
+ }
+ if (!native.empty()) {
+ makeCommand += sep;
+ makeCommand += native;
+ }
+ return makeCommand;
+}
+
+void cmGlobalGenerator::AddMakefile(std::unique_ptr<cmMakefile> mf)
+{
+ this->IndexMakefile(mf.get());
+ this->Makefiles.push_back(std::move(mf));
+
+ // update progress
+ // estimate how many lg there will be
+ const std::string* numGenC =
+ this->CMakeInstance->GetState()->GetInitializedCacheValue(
+ "CMAKE_NUMBER_OF_MAKEFILES");
+
+ if (!numGenC) {
+ // If CMAKE_NUMBER_OF_MAKEFILES is not set
+ // we are in the first time progress and we have no
+ // idea how long it will be. So, just move half way
+ // there each time, and don't go over 95%
+ this->FirstTimeProgress += ((1.0f - this->FirstTimeProgress) / 30.0f);
+ if (this->FirstTimeProgress > 0.95f) {
+ this->FirstTimeProgress = 0.95f;
+ }
+ this->CMakeInstance->UpdateProgress("Configuring",
+ this->FirstTimeProgress);
+ return;
+ }
+
+ int numGen = atoi(numGenC->c_str());
+ float prog =
+ static_cast<float>(this->Makefiles.size()) / static_cast<float>(numGen);
+ if (prog > 1.0f) {
+ prog = 1.0f;
+ }
+ this->CMakeInstance->UpdateProgress("Configuring", prog);
+}
+
+void cmGlobalGenerator::AddInstallComponent(const std::string& component)
+{
+ if (!component.empty()) {
+ this->InstallComponents.insert(component);
+ }
+}
+
+void cmGlobalGenerator::EnableInstallTarget()
+{
+ this->InstallTargetEnabled = true;
+}
+
+std::unique_ptr<cmLocalGenerator> cmGlobalGenerator::CreateLocalGenerator(
+ cmMakefile* mf)
+{
+ return cm::make_unique<cmLocalGenerator>(this, mf);
+}
+
+void cmGlobalGenerator::EnableLanguagesFromGenerator(cmGlobalGenerator* gen,
+ cmMakefile* mf)
+{
+ this->SetConfiguredFilesPath(gen);
+ this->TryCompileOuterMakefile = mf;
+ const char* make =
+ gen->GetCMakeInstance()->GetCacheDefinition("CMAKE_MAKE_PROGRAM");
+ this->GetCMakeInstance()->AddCacheEntry(
+ "CMAKE_MAKE_PROGRAM", make, "make program", cmStateEnums::FILEPATH);
+ // copy the enabled languages
+ this->GetCMakeInstance()->GetState()->SetEnabledLanguages(
+ gen->GetCMakeInstance()->GetState()->GetEnabledLanguages());
+ this->LanguagesReady = gen->LanguagesReady;
+ this->ExtensionToLanguage = gen->ExtensionToLanguage;
+ this->IgnoreExtensions = gen->IgnoreExtensions;
+ this->LanguageToOutputExtension = gen->LanguageToOutputExtension;
+ this->LanguageToLinkerPreference = gen->LanguageToLinkerPreference;
+ this->OutputExtensions = gen->OutputExtensions;
+}
+
+void cmGlobalGenerator::SetConfiguredFilesPath(cmGlobalGenerator* gen)
+{
+ if (!gen->ConfiguredFilesPath.empty()) {
+ this->ConfiguredFilesPath = gen->ConfiguredFilesPath;
+ } else {
+ this->ConfiguredFilesPath =
+ cmStrCat(gen->CMakeInstance->GetHomeOutputDirectory(), "/CMakeFiles");
+ }
+}
+
+bool cmGlobalGenerator::IsExcluded(cmStateSnapshot const& rootSnp,
+ cmStateSnapshot const& snp_) const
+{
+ cmStateSnapshot snp = snp_;
+ while (snp.IsValid()) {
+ if (snp == rootSnp) {
+ // No directory excludes itself.
+ return false;
+ }
+
+ if (snp.GetDirectory().GetPropertyAsBool("EXCLUDE_FROM_ALL")) {
+ // This directory is excluded from its parent.
+ return true;
+ }
+ snp = snp.GetBuildsystemDirectoryParent();
+ }
+ return false;
+}
+
+bool cmGlobalGenerator::IsExcluded(cmLocalGenerator* root,
+ cmLocalGenerator* gen) const
+{
+ assert(gen);
+
+ cmStateSnapshot rootSnp = root->GetStateSnapshot();
+ cmStateSnapshot snp = gen->GetStateSnapshot();
+
+ return this->IsExcluded(rootSnp, snp);
+}
+
+bool cmGlobalGenerator::IsExcluded(cmLocalGenerator* root,
+ cmGeneratorTarget* target) const
+{
+ if (target->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
+ return true;
+ }
+ if (const char* exclude = target->GetProperty("EXCLUDE_FROM_ALL")) {
+ return cmIsOn(exclude);
+ }
+ // This target is included in its directory. Check whether the
+ // directory is excluded.
+ return this->IsExcluded(root, target->GetLocalGenerator());
+}
+
+void cmGlobalGenerator::GetEnabledLanguages(
+ std::vector<std::string>& lang) const
+{
+ lang = this->CMakeInstance->GetState()->GetEnabledLanguages();
+}
+
+int cmGlobalGenerator::GetLinkerPreference(const std::string& lang) const
+{
+ auto const it = this->LanguageToLinkerPreference.find(lang);
+ if (it != this->LanguageToLinkerPreference.end()) {
+ return it->second;
+ }
+ return 0;
+}
+
+void cmGlobalGenerator::FillProjectMap()
+{
+ this->ProjectMap.clear(); // make sure we start with a clean map
+ for (const auto& localGen : this->LocalGenerators) {
+ // for each local generator add all projects
+ cmStateSnapshot snp = localGen->GetStateSnapshot();
+ std::string name;
+ do {
+ std::string snpProjName = snp.GetProjectName();
+ if (name != snpProjName) {
+ name = snpProjName;
+ this->ProjectMap[name].push_back(localGen.get());
+ }
+ snp = snp.GetBuildsystemDirectoryParent();
+ } while (snp.IsValid());
+ }
+}
+
+cmMakefile* cmGlobalGenerator::FindMakefile(const std::string& start_dir) const
+{
+ auto const it = this->MakefileSearchIndex.find(start_dir);
+ if (it != this->MakefileSearchIndex.end()) {
+ return it->second;
+ }
+ return nullptr;
+}
+
+cmLocalGenerator* cmGlobalGenerator::FindLocalGenerator(
+ cmDirectoryId const& id) const
+{
+ auto const it = this->LocalGeneratorSearchIndex.find(id.String);
+ if (it != this->LocalGeneratorSearchIndex.end()) {
+ return it->second;
+ }
+ return nullptr;
+}
+
+void cmGlobalGenerator::AddAlias(const std::string& name,
+ std::string const& tgtName)
+{
+ this->AliasTargets[name] = tgtName;
+}
+
+bool cmGlobalGenerator::IsAlias(const std::string& name) const
+{
+ return cmContains(this->AliasTargets, name);
+}
+
+void cmGlobalGenerator::IndexTarget(cmTarget* t)
+{
+ if (!t->IsImported() || t->IsImportedGloballyVisible()) {
+ this->TargetSearchIndex[t->GetName()] = t;
+ }
+}
+
+void cmGlobalGenerator::IndexGeneratorTarget(cmGeneratorTarget* gt)
+{
+ if (!gt->IsImported() || gt->IsImportedGloballyVisible()) {
+ this->GeneratorTargetSearchIndex[gt->GetName()] = gt;
+ }
+}
+
+static char const hexDigits[] = "0123456789abcdef";
+
+std::string cmGlobalGenerator::IndexGeneratorTargetUniquely(
+ cmGeneratorTarget const* gt)
+{
+ // Use the pointer value to uniquely identify the target instance.
+ // Use a ":" prefix to avoid conflict with project-defined targets.
+ // We must satisfy cmGeneratorExpression::IsValidTargetName so use no
+ // other special characters.
+ char buf[1 + sizeof(gt) * 2];
+ char* b = buf;
+ *b++ = ':';
+ for (size_t i = 0; i < sizeof(gt); ++i) {
+ unsigned char const c = reinterpret_cast<unsigned char const*>(&gt)[i];
+ *b++ = hexDigits[(c & 0xf0) >> 4];
+ *b++ = hexDigits[(c & 0x0f)];
+ }
+ std::string id(buf, sizeof(buf));
+ // We internally index pointers to non-const generator targets
+ // but our callers only have pointers to const generator targets.
+ // They will give up non-const privileges when looking up anyway.
+ this->GeneratorTargetSearchIndex[id] = const_cast<cmGeneratorTarget*>(gt);
+ return id;
+}
+
+void cmGlobalGenerator::IndexMakefile(cmMakefile* mf)
+{
+ // FIXME: add_subdirectory supports multiple build directories
+ // sharing the same source directory. We currently index only the
+ // first one, because that is what FindMakefile has always returned.
+ // All of its callers will need to be modified to support looking
+ // up directories by build directory path.
+ this->MakefileSearchIndex.insert(
+ MakefileMap::value_type(mf->GetCurrentSourceDirectory(), mf));
+}
+
+void cmGlobalGenerator::IndexLocalGenerator(cmLocalGenerator* lg)
+{
+ cmDirectoryId id = lg->GetMakefile()->GetDirectoryId();
+ this->LocalGeneratorSearchIndex[id.String] = lg;
+}
+
+cmTarget* cmGlobalGenerator::FindTargetImpl(std::string const& name) const
+{
+ auto const it = this->TargetSearchIndex.find(name);
+ if (it != this->TargetSearchIndex.end()) {
+ return it->second;
+ }
+ return nullptr;
+}
+
+cmGeneratorTarget* cmGlobalGenerator::FindGeneratorTargetImpl(
+ std::string const& name) const
+{
+ auto const it = this->GeneratorTargetSearchIndex.find(name);
+ if (it != this->GeneratorTargetSearchIndex.end()) {
+ return it->second;
+ }
+ return nullptr;
+}
+
+cmTarget* cmGlobalGenerator::FindTarget(const std::string& name,
+ bool excludeAliases) const
+{
+ if (!excludeAliases) {
+ auto const ai = this->AliasTargets.find(name);
+ if (ai != this->AliasTargets.end()) {
+ return this->FindTargetImpl(ai->second);
+ }
+ }
+ return this->FindTargetImpl(name);
+}
+
+cmGeneratorTarget* cmGlobalGenerator::FindGeneratorTarget(
+ const std::string& name) const
+{
+ auto const ai = this->AliasTargets.find(name);
+ if (ai != this->AliasTargets.end()) {
+ return this->FindGeneratorTargetImpl(ai->second);
+ }
+ return this->FindGeneratorTargetImpl(name);
+}
+
+bool cmGlobalGenerator::NameResolvesToFramework(
+ const std::string& libname) const
+{
+ if (cmSystemTools::IsPathToFramework(libname)) {
+ return true;
+ }
+
+ if (cmTarget* tgt = this->FindTarget(libname)) {
+ if (tgt->IsFrameworkOnApple()) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool cmGlobalGenerator::CheckCMP0037(std::string const& targetName,
+ std::string const& reason) const
+{
+ cmTarget* tgt = this->FindTarget(targetName);
+ if (!tgt) {
+ return true;
+ }
+ MessageType messageType = MessageType::AUTHOR_WARNING;
+ std::ostringstream e;
+ bool issueMessage = false;
+ switch (tgt->GetPolicyStatusCMP0037()) {
+ case cmPolicies::WARN:
+ e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0037) << "\n";
+ issueMessage = true;
+ CM_FALLTHROUGH;
+ case cmPolicies::OLD:
+ break;
+ case cmPolicies::NEW:
+ case cmPolicies::REQUIRED_IF_USED:
+ case cmPolicies::REQUIRED_ALWAYS:
+ issueMessage = true;
+ messageType = MessageType::FATAL_ERROR;
+ break;
+ }
+ if (issueMessage) {
+ e << "The target name \"" << targetName << "\" is reserved " << reason
+ << ".";
+ if (messageType == MessageType::AUTHOR_WARNING) {
+ e << " It may result in undefined behavior.";
+ }
+ this->GetCMakeInstance()->IssueMessage(messageType, e.str(),
+ tgt->GetBacktrace());
+ if (messageType == MessageType::FATAL_ERROR) {
+ return false;
+ }
+ }
+ return true;
+}
+
+void cmGlobalGenerator::CreateDefaultGlobalTargets(
+ std::vector<GlobalTargetInfo>& targets)
+{
+ this->AddGlobalTarget_Package(targets);
+ this->AddGlobalTarget_PackageSource(targets);
+ this->AddGlobalTarget_Test(targets);
+ this->AddGlobalTarget_EditCache(targets);
+ this->AddGlobalTarget_RebuildCache(targets);
+ this->AddGlobalTarget_Install(targets);
+}
+
+void cmGlobalGenerator::AddGlobalTarget_Package(
+ std::vector<GlobalTargetInfo>& targets)
+{
+ auto& mf = this->Makefiles[0];
+ std::string configFile =
+ cmStrCat(mf->GetCurrentBinaryDirectory(), "/CPackConfig.cmake");
+ if (!cmSystemTools::FileExists(configFile)) {
+ return;
+ }
+
+ static const auto reservedTargets = { "package", "PACKAGE" };
+ for (auto const& target : reservedTargets) {
+ if (!this->CheckCMP0037(target, "when CPack packaging is enabled")) {
+ return;
+ }
+ }
+
+ const char* cmakeCfgIntDir = this->GetCMakeCFGIntDir();
+ GlobalTargetInfo gti;
+ gti.Name = this->GetPackageTargetName();
+ gti.Message = "Run CPack packaging tool...";
+ gti.UsesTerminal = true;
+ gti.WorkingDir = mf->GetCurrentBinaryDirectory();
+ cmCustomCommandLine singleLine;
+ singleLine.push_back(cmSystemTools::GetCPackCommand());
+ if (cmakeCfgIntDir && *cmakeCfgIntDir && cmakeCfgIntDir[0] != '.') {
+ singleLine.push_back("-C");
+ singleLine.push_back(cmakeCfgIntDir);
+ }
+ singleLine.push_back("--config");
+ singleLine.push_back("./CPackConfig.cmake");
+ gti.CommandLines.push_back(std::move(singleLine));
+ if (this->GetPreinstallTargetName()) {
+ gti.Depends.emplace_back(this->GetPreinstallTargetName());
+ } else {
+ const char* noPackageAll =
+ mf->GetDefinition("CMAKE_SKIP_PACKAGE_ALL_DEPENDENCY");
+ if (!noPackageAll || cmIsOff(noPackageAll)) {
+ gti.Depends.emplace_back(this->GetAllTargetName());
+ }
+ }
+ targets.push_back(std::move(gti));
+}
+
+void cmGlobalGenerator::AddGlobalTarget_PackageSource(
+ std::vector<GlobalTargetInfo>& targets)
+{
+ const char* packageSourceTargetName = this->GetPackageSourceTargetName();
+ if (!packageSourceTargetName) {
+ return;
+ }
+
+ auto& mf = this->Makefiles[0];
+ std::string configFile =
+ cmStrCat(mf->GetCurrentBinaryDirectory(), "/CPackSourceConfig.cmake");
+ if (!cmSystemTools::FileExists(configFile)) {
+ return;
+ }
+
+ static const auto reservedTargets = { "package_source" };
+ for (auto const& target : reservedTargets) {
+ if (!this->CheckCMP0037(target,
+ "when CPack source packaging is enabled")) {
+ return;
+ }
+ }
+
+ GlobalTargetInfo gti;
+ gti.Name = packageSourceTargetName;
+ gti.Message = "Run CPack packaging tool for source...";
+ gti.WorkingDir = mf->GetCurrentBinaryDirectory();
+ gti.UsesTerminal = true;
+ cmCustomCommandLine singleLine;
+ singleLine.push_back(cmSystemTools::GetCPackCommand());
+ singleLine.push_back("--config");
+ singleLine.push_back("./CPackSourceConfig.cmake");
+ singleLine.push_back(std::move(configFile));
+ gti.CommandLines.push_back(std::move(singleLine));
+ targets.push_back(std::move(gti));
+}
+
+void cmGlobalGenerator::AddGlobalTarget_Test(
+ std::vector<GlobalTargetInfo>& targets)
+{
+ auto& mf = this->Makefiles[0];
+ if (!mf->IsOn("CMAKE_TESTING_ENABLED")) {
+ return;
+ }
+
+ static const auto reservedTargets = { "test", "RUN_TESTS" };
+ for (auto const& target : reservedTargets) {
+ if (!this->CheckCMP0037(target, "when CTest testing is enabled")) {
+ return;
+ }
+ }
+
+ const char* cmakeCfgIntDir = this->GetCMakeCFGIntDir();
+ GlobalTargetInfo gti;
+ gti.Name = this->GetTestTargetName();
+ gti.Message = "Running tests...";
+ gti.UsesTerminal = true;
+ cmCustomCommandLine singleLine;
+ singleLine.push_back(cmSystemTools::GetCTestCommand());
+ singleLine.push_back("--force-new-ctest-process");
+ if (auto testArgs = mf->GetDefinition("CMAKE_CTEST_ARGUMENTS")) {
+ std::vector<std::string> args;
+ cmExpandList(testArgs, args);
+ for (auto const& arg : args) {
+ singleLine.push_back(arg);
+ }
+ }
+ if (cmakeCfgIntDir && *cmakeCfgIntDir && cmakeCfgIntDir[0] != '.') {
+ singleLine.push_back("-C");
+ singleLine.push_back(cmakeCfgIntDir);
+ } else // TODO: This is a hack. Should be something to do with the
+ // generator
+ {
+ singleLine.push_back("$(ARGS)");
+ }
+ gti.CommandLines.push_back(std::move(singleLine));
+ targets.push_back(std::move(gti));
+}
+
+void cmGlobalGenerator::AddGlobalTarget_EditCache(
+ std::vector<GlobalTargetInfo>& targets)
+{
+ const char* editCacheTargetName = this->GetEditCacheTargetName();
+ if (!editCacheTargetName) {
+ return;
+ }
+ GlobalTargetInfo gti;
+ gti.Name = editCacheTargetName;
+ gti.PerConfig = false;
+ cmCustomCommandLine singleLine;
+
+ // Use generator preference for the edit_cache rule if it is defined.
+ std::string edit_cmd = this->GetEditCacheCommand();
+ if (!edit_cmd.empty()) {
+ singleLine.push_back(std::move(edit_cmd));
+ singleLine.push_back("-S$(CMAKE_SOURCE_DIR)");
+ singleLine.push_back("-B$(CMAKE_BINARY_DIR)");
+ gti.Message = "Running CMake cache editor...";
+ gti.UsesTerminal = true;
+ } else {
+ singleLine.push_back(cmSystemTools::GetCMakeCommand());
+ singleLine.push_back("-E");
+ singleLine.push_back("echo");
+ singleLine.push_back("No interactive CMake dialog available.");
+ gti.Message = "No interactive CMake dialog available...";
+ gti.UsesTerminal = false;
+ }
+ gti.CommandLines.push_back(std::move(singleLine));
+
+ targets.push_back(std::move(gti));
+}
+
+void cmGlobalGenerator::AddGlobalTarget_RebuildCache(
+ std::vector<GlobalTargetInfo>& targets)
+{
+ const char* rebuildCacheTargetName = this->GetRebuildCacheTargetName();
+ if (!rebuildCacheTargetName) {
+ return;
+ }
+ GlobalTargetInfo gti;
+ gti.Name = rebuildCacheTargetName;
+ gti.Message = "Running CMake to regenerate build system...";
+ gti.UsesTerminal = true;
+ gti.PerConfig = false;
+ cmCustomCommandLine singleLine;
+ singleLine.push_back(cmSystemTools::GetCMakeCommand());
+ singleLine.push_back("--regenerate-during-build");
+ singleLine.push_back("-S$(CMAKE_SOURCE_DIR)");
+ singleLine.push_back("-B$(CMAKE_BINARY_DIR)");
+ gti.CommandLines.push_back(std::move(singleLine));
+ targets.push_back(std::move(gti));
+}
+
+void cmGlobalGenerator::AddGlobalTarget_Install(
+ std::vector<GlobalTargetInfo>& targets)
+{
+ auto& mf = this->Makefiles[0];
+ const char* cmakeCfgIntDir = this->GetCMakeCFGIntDir();
+ bool skipInstallRules = mf->IsOn("CMAKE_SKIP_INSTALL_RULES");
+ if (this->InstallTargetEnabled && skipInstallRules) {
+ this->CMakeInstance->IssueMessage(
+ MessageType::WARNING,
+ "CMAKE_SKIP_INSTALL_RULES was enabled even though "
+ "installation rules have been specified",
+ mf->GetBacktrace());
+ } else if (this->InstallTargetEnabled && !skipInstallRules) {
+ if (!cmakeCfgIntDir || !*cmakeCfgIntDir || cmakeCfgIntDir[0] == '.') {
+ std::set<std::string>* componentsSet = &this->InstallComponents;
+ std::ostringstream ostr;
+ if (!componentsSet->empty()) {
+ ostr << "Available install components are: ";
+ ostr << cmWrap('"', *componentsSet, '"', " ");
+ } else {
+ ostr << "Only default component available";
+ }
+ GlobalTargetInfo gti;
+ gti.Name = "list_install_components";
+ gti.Message = ostr.str();
+ gti.UsesTerminal = false;
+ targets.push_back(std::move(gti));
+ }
+ std::string cmd = cmSystemTools::GetCMakeCommand();
+ GlobalTargetInfo gti;
+ gti.Name = this->GetInstallTargetName();
+ gti.Message = "Install the project...";
+ gti.UsesTerminal = true;
+ cmCustomCommandLine singleLine;
+ if (this->GetPreinstallTargetName()) {
+ gti.Depends.emplace_back(this->GetPreinstallTargetName());
+ } else {
+ const char* noall =
+ mf->GetDefinition("CMAKE_SKIP_INSTALL_ALL_DEPENDENCY");
+ if (!noall || cmIsOff(noall)) {
+ gti.Depends.emplace_back(this->GetAllTargetName());
+ }
+ }
+ if (mf->GetDefinition("CMake_BINARY_DIR") &&
+ !mf->IsOn("CMAKE_CROSSCOMPILING")) {
+ // We are building CMake itself. We cannot use the original
+ // executable to install over itself. The generator will
+ // automatically convert this name to the build-time location.
+ cmd = "cmake";
+ }
+ singleLine.push_back(cmd);
+ if (cmakeCfgIntDir && *cmakeCfgIntDir && cmakeCfgIntDir[0] != '.') {
+ std::string cfgArg = "-DBUILD_TYPE=";
+ bool useEPN = this->UseEffectivePlatformName(mf.get());
+ if (useEPN) {
+ cfgArg += "$(CONFIGURATION)";
+ singleLine.push_back(cfgArg);
+ cfgArg = "-DEFFECTIVE_PLATFORM_NAME=$(EFFECTIVE_PLATFORM_NAME)";
+ } else {
+ cfgArg += mf->GetDefinition("CMAKE_CFG_INTDIR");
+ }
+ singleLine.push_back(cfgArg);
+ }
+ singleLine.push_back("-P");
+ singleLine.push_back("cmake_install.cmake");
+ gti.CommandLines.push_back(singleLine);
+ targets.push_back(gti);
+
+ // install_local
+ if (const char* install_local = this->GetInstallLocalTargetName()) {
+ gti.Name = install_local;
+ gti.Message = "Installing only the local directory...";
+ gti.UsesTerminal = true;
+ gti.CommandLines.clear();
+
+ cmCustomCommandLine localCmdLine = singleLine;
+
+ localCmdLine.insert(localCmdLine.begin() + 1,
+ "-DCMAKE_INSTALL_LOCAL_ONLY=1");
+
+ gti.CommandLines.push_back(std::move(localCmdLine));
+ targets.push_back(gti);
+ }
+
+ // install_strip
+ const char* install_strip = this->GetInstallStripTargetName();
+ if ((install_strip != nullptr) && (mf->IsSet("CMAKE_STRIP"))) {
+ gti.Name = install_strip;
+ gti.Message = "Installing the project stripped...";
+ gti.UsesTerminal = true;
+ gti.CommandLines.clear();
+
+ cmCustomCommandLine stripCmdLine = singleLine;
+
+ stripCmdLine.insert(stripCmdLine.begin() + 1,
+ "-DCMAKE_INSTALL_DO_STRIP=1");
+ gti.CommandLines.push_back(std::move(stripCmdLine));
+ targets.push_back(gti);
+ }
+ }
+}
+
+std::string cmGlobalGenerator::GetPredefinedTargetsFolder()
+{
+ cmProp prop = this->GetCMakeInstance()->GetState()->GetGlobalProperty(
+ "PREDEFINED_TARGETS_FOLDER");
+
+ if (prop) {
+ return *prop;
+ }
+
+ return "CMakePredefinedTargets";
+}
+
+bool cmGlobalGenerator::UseFolderProperty() const
+{
+ cmProp prop =
+ this->GetCMakeInstance()->GetState()->GetGlobalProperty("USE_FOLDERS");
+
+ // If this property is defined, let the setter turn this on or off...
+ //
+ if (prop) {
+ return cmIsOn(*prop);
+ }
+
+ // By default, this feature is OFF, since it is not supported in the
+ // Visual Studio Express editions until VS11:
+ //
+ return false;
+}
+
+cmTarget cmGlobalGenerator::CreateGlobalTarget(GlobalTargetInfo const& gti,
+ cmMakefile* mf)
+{
+ // Package
+ cmTarget target(gti.Name, cmStateEnums::GLOBAL_TARGET,
+ cmTarget::VisibilityNormal, mf, gti.PerConfig);
+ target.SetProperty("EXCLUDE_FROM_ALL", "TRUE");
+
+ std::vector<std::string> no_outputs;
+ std::vector<std::string> no_byproducts;
+ std::vector<std::string> no_depends;
+ // Store the custom command in the target.
+ cmCustomCommand cc(no_outputs, no_byproducts, no_depends, gti.CommandLines,
+ cmListFileBacktrace(), nullptr, gti.WorkingDir.c_str());
+ cc.SetUsesTerminal(gti.UsesTerminal);
+ target.AddPostBuildCommand(std::move(cc));
+ if (!gti.Message.empty()) {
+ target.SetProperty("EchoString", gti.Message);
+ }
+ for (std::string const& d : gti.Depends) {
+ target.AddUtility(d, false);
+ }
+
+ // Organize in the "predefined targets" folder:
+ //
+ if (this->UseFolderProperty()) {
+ target.SetProperty("FOLDER", this->GetPredefinedTargetsFolder());
+ }
+
+ return target;
+}
+
+std::string cmGlobalGenerator::GenerateRuleFile(
+ std::string const& output) const
+{
+ std::string ruleFile = cmStrCat(output, ".rule");
+ const char* dir = this->GetCMakeCFGIntDir();
+ if (dir && dir[0] == '$') {
+ cmSystemTools::ReplaceString(ruleFile, dir, "/CMakeFiles");
+ }
+ return ruleFile;
+}
+
+bool cmGlobalGenerator::ShouldStripResourcePath(cmMakefile* mf) const
+{
+ return mf->PlatformIsAppleEmbedded();
+}
+
+std::string cmGlobalGenerator::GetSharedLibFlagsForLanguage(
+ std::string const& l) const
+{
+ auto const it = this->LanguageToOriginalSharedLibFlags.find(l);
+ if (it != this->LanguageToOriginalSharedLibFlags.end()) {
+ return it->second;
+ }
+ return "";
+}
+
+void cmGlobalGenerator::AppendDirectoryForConfig(const std::string& /*unused*/,
+ const std::string& /*unused*/,
+ const std::string& /*unused*/,
+ std::string& /*unused*/)
+{
+ // Subclasses that support multiple configurations should implement
+ // this method to append the subdirectory for the given build
+ // configuration.
+}
+
+cmGlobalGenerator::TargetDependSet const&
+cmGlobalGenerator::GetTargetDirectDepends(cmGeneratorTarget const* target)
+{
+ return this->TargetDependencies[target];
+}
+
+bool cmGlobalGenerator::IsReservedTarget(std::string const& name)
+{
+ // The following is a list of targets reserved
+ // by one or more of the cmake generators.
+
+ // Adding additional targets to this list will require a policy!
+ const char* reservedTargets[] = { "all", "ALL_BUILD", "help",
+ "install", "INSTALL", "preinstall",
+ "clean", "edit_cache", "rebuild_cache",
+ "ZERO_CHECK" };
+
+ return cmContains(reservedTargets, name);
+}
+
+void cmGlobalGenerator::SetExternalMakefileProjectGenerator(
+ std::unique_ptr<cmExternalMakefileProjectGenerator> extraGenerator)
+{
+ this->ExtraGenerator = std::move(extraGenerator);
+ if (this->ExtraGenerator) {
+ this->ExtraGenerator->SetGlobalGenerator(this);
+ }
+}
+
+std::string cmGlobalGenerator::GetExtraGeneratorName() const
+{
+ return this->ExtraGenerator ? this->ExtraGenerator->GetName()
+ : std::string();
+}
+
+void cmGlobalGenerator::FileReplacedDuringGenerate(const std::string& filename)
+{
+ this->FilesReplacedDuringGenerate.push_back(filename);
+}
+
+void cmGlobalGenerator::GetFilesReplacedDuringGenerate(
+ std::vector<std::string>& filenames)
+{
+ filenames.clear();
+ std::copy(this->FilesReplacedDuringGenerate.begin(),
+ this->FilesReplacedDuringGenerate.end(),
+ std::back_inserter(filenames));
+}
+
+void cmGlobalGenerator::GetTargetSets(
+ TargetDependSet& projectTargets, TargetDependSet& originalTargets,
+ cmLocalGenerator* root, std::vector<cmLocalGenerator*>& generators)
+{
+ // loop over all local generators
+ for (auto generator : generators) {
+ // check to make sure generator is not excluded
+ if (this->IsExcluded(root, generator)) {
+ continue;
+ }
+ // loop over all the generator targets in the makefile
+ for (const auto& target : generator->GetGeneratorTargets()) {
+ if (this->IsRootOnlyTarget(target.get()) &&
+ target->GetLocalGenerator() != root) {
+ continue;
+ }
+ // put the target in the set of original targets
+ originalTargets.insert(target.get());
+ // Get the set of targets that depend on target
+ this->AddTargetDepends(target.get(), projectTargets);
+ }
+ }
+}
+
+bool cmGlobalGenerator::IsRootOnlyTarget(cmGeneratorTarget* target) const
+{
+ return (target->GetType() == cmStateEnums::GLOBAL_TARGET ||
+ target->GetName() == this->GetAllTargetName());
+}
+
+void cmGlobalGenerator::AddTargetDepends(cmGeneratorTarget const* target,
+ TargetDependSet& projectTargets)
+{
+ // add the target itself
+ if (projectTargets.insert(target).second) {
+ // This is the first time we have encountered the target.
+ // Recursively follow its dependencies.
+ for (auto const& t : this->GetTargetDirectDepends(target)) {
+ this->AddTargetDepends(t, projectTargets);
+ }
+ }
+}
+
+void cmGlobalGenerator::AddToManifest(std::string const& f)
+{
+ // Add to the content listing for the file's directory.
+ std::string dir = cmSystemTools::GetFilenamePath(f);
+ std::string file = cmSystemTools::GetFilenameName(f);
+ DirectoryContent& dc = this->DirectoryContentMap[dir];
+ dc.Generated.insert(file);
+ dc.All.insert(file);
+}
+
+std::set<std::string> const& cmGlobalGenerator::GetDirectoryContent(
+ std::string const& dir, bool needDisk)
+{
+ DirectoryContent& dc = this->DirectoryContentMap[dir];
+ if (needDisk) {
+ long mt = cmSystemTools::ModifiedTime(dir);
+ if (mt != dc.LastDiskTime) {
+ // Reset to non-loaded directory content.
+ dc.All = dc.Generated;
+
+ // Load the directory content from disk.
+ cmsys::Directory d;
+ if (d.Load(dir)) {
+ unsigned long n = d.GetNumberOfFiles();
+ for (unsigned long i = 0; i < n; ++i) {
+ const char* f = d.GetFile(i);
+ if (strcmp(f, ".") != 0 && strcmp(f, "..") != 0) {
+ dc.All.insert(f);
+ }
+ }
+ }
+ dc.LastDiskTime = mt;
+ }
+ }
+ return dc.All;
+}
+
+void cmGlobalGenerator::AddRuleHash(const std::vector<std::string>& outputs,
+ std::string const& content)
+{
+#if !defined(CMAKE_BOOTSTRAP)
+ // Ignore if there are no outputs.
+ if (outputs.empty()) {
+ return;
+ }
+
+ // Compute a hash of the rule.
+ RuleHash hash;
+ {
+ cmCryptoHash md5(cmCryptoHash::AlgoMD5);
+ std::string const md5_hex = md5.HashString(content);
+ memcpy(hash.Data, md5_hex.c_str(), 32);
+ }
+
+ // Shorten the output name (in expected use case).
+ cmStateDirectory cmDir =
+ this->GetMakefiles()[0]->GetStateSnapshot().GetDirectory();
+ std::string fname = cmDir.ConvertToRelPathIfNotContained(
+ this->GetMakefiles()[0]->GetState()->GetBinaryDirectory(), outputs[0]);
+
+ // Associate the hash with this output.
+ this->RuleHashes[fname] = hash;
+#else
+ (void)outputs;
+ (void)content;
+#endif
+}
+
+void cmGlobalGenerator::CheckRuleHashes()
+{
+#if !defined(CMAKE_BOOTSTRAP)
+ std::string home = this->GetCMakeInstance()->GetHomeOutputDirectory();
+ std::string pfile = cmStrCat(home, "/CMakeFiles/CMakeRuleHashes.txt");
+ this->CheckRuleHashes(pfile, home);
+ this->WriteRuleHashes(pfile);
+#endif
+}
+
+void cmGlobalGenerator::CheckRuleHashes(std::string const& pfile,
+ std::string const& home)
+{
+#if defined(_WIN32) || defined(__CYGWIN__)
+ cmsys::ifstream fin(pfile.c_str(), std::ios::in | std::ios::binary);
+#else
+ cmsys::ifstream fin(pfile.c_str());
+#endif
+ if (!fin) {
+ return;
+ }
+ std::string line;
+ std::string fname;
+ while (cmSystemTools::GetLineFromStream(fin, line)) {
+ // Line format is a 32-byte hex string followed by a space
+ // followed by a file name (with no escaping).
+
+ // Skip blank and comment lines.
+ if (line.size() < 34 || line[0] == '#') {
+ continue;
+ }
+
+ // Get the filename.
+ fname = line.substr(33);
+
+ // Look for a hash for this file's rule.
+ auto const rhi = this->RuleHashes.find(fname);
+ if (rhi != this->RuleHashes.end()) {
+ // Compare the rule hash in the file to that we were given.
+ if (strncmp(line.c_str(), rhi->second.Data, 32) != 0) {
+ // The rule has changed. Delete the output so it will be
+ // built again.
+ fname = cmSystemTools::CollapseFullPath(fname, home);
+ cmSystemTools::RemoveFile(fname);
+ }
+ } else {
+ // We have no hash for a rule previously listed. This may be a
+ // case where a user has turned off a build option and might
+ // want to turn it back on later, so do not delete the file.
+ // Instead, we keep the rule hash as long as the file exists so
+ // that if the feature is turned back on and the rule has
+ // changed the file is still rebuilt.
+ std::string fpath = cmSystemTools::CollapseFullPath(fname, home);
+ if (cmSystemTools::FileExists(fpath)) {
+ RuleHash hash;
+ memcpy(hash.Data, line.c_str(), 32);
+ this->RuleHashes[fname] = hash;
+ }
+ }
+ }
+}
+
+void cmGlobalGenerator::WriteRuleHashes(std::string const& pfile)
+{
+ // Now generate a new persistence file with the current hashes.
+ if (this->RuleHashes.empty()) {
+ cmSystemTools::RemoveFile(pfile);
+ } else {
+ cmGeneratedFileStream fout(pfile);
+ fout << "# Hashes of file build rules.\n";
+ for (auto const& rh : this->RuleHashes) {
+ fout.write(rh.second.Data, 32);
+ fout << " " << rh.first << "\n";
+ }
+ }
+}
+
+void cmGlobalGenerator::WriteSummary()
+{
+ // Record all target directories in a central location.
+ std::string fname = cmStrCat(this->CMakeInstance->GetHomeOutputDirectory(),
+ "/CMakeFiles/TargetDirectories.txt");
+ cmGeneratedFileStream fout(fname);
+
+ for (const auto& lg : this->LocalGenerators) {
+ for (const auto& tgt : lg->GetGeneratorTargets()) {
+ if (tgt->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
+ continue;
+ }
+ this->WriteSummary(tgt.get());
+ fout << tgt->GetSupportDirectory() << "\n";
+ }
+ }
+}
+
+void cmGlobalGenerator::WriteSummary(cmGeneratorTarget* target)
+{
+ // Place the labels file in a per-target support directory.
+ std::string dir = target->GetSupportDirectory();
+ std::string file = cmStrCat(dir, "/Labels.txt");
+ std::string json_file = dir + "/Labels.json";
+
+#ifndef CMAKE_BOOTSTRAP
+ // Check whether labels are enabled for this target.
+ const char* targetLabels = target->GetProperty("LABELS");
+ cmProp directoryLabels =
+ target->Target->GetMakefile()->GetProperty("LABELS");
+ const char* cmakeDirectoryLabels =
+ target->Target->GetMakefile()->GetDefinition("CMAKE_DIRECTORY_LABELS");
+ if (targetLabels || directoryLabels || cmakeDirectoryLabels) {
+ Json::Value lj_root(Json::objectValue);
+ Json::Value& lj_target = lj_root["target"] = Json::objectValue;
+ lj_target["name"] = target->GetName();
+ Json::Value& lj_target_labels = lj_target["labels"] = Json::arrayValue;
+ Json::Value& lj_sources = lj_root["sources"] = Json::arrayValue;
+
+ cmSystemTools::MakeDirectory(dir);
+ cmGeneratedFileStream fout(file);
+
+ std::vector<std::string> labels;
+
+ // List the target-wide labels. All sources in the target get
+ // these labels.
+ if (targetLabels) {
+ cmExpandList(targetLabels, labels);
+ if (!labels.empty()) {
+ fout << "# Target labels\n";
+ for (std::string const& l : labels) {
+ fout << " " << l << "\n";
+ lj_target_labels.append(l);
+ }
+ }
+ }
+
+ // List directory labels
+ std::vector<std::string> directoryLabelsList;
+ std::vector<std::string> cmakeDirectoryLabelsList;
+
+ if (directoryLabels) {
+ cmExpandList(*directoryLabels, directoryLabelsList);
+ }
+
+ if (cmakeDirectoryLabels) {
+ cmExpandList(cmakeDirectoryLabels, cmakeDirectoryLabelsList);
+ }
+
+ if (!directoryLabelsList.empty() || !cmakeDirectoryLabelsList.empty()) {
+ fout << "# Directory labels\n";
+ }
+
+ for (std::string const& li : directoryLabelsList) {
+ fout << " " << li << "\n";
+ lj_target_labels.append(li);
+ }
+
+ for (std::string const& li : cmakeDirectoryLabelsList) {
+ fout << " " << li << "\n";
+ lj_target_labels.append(li);
+ }
+
+ // List the source files with any per-source labels.
+ fout << "# Source files and their labels\n";
+ std::vector<cmSourceFile*> sources;
+ std::vector<std::string> const& configs =
+ target->Target->GetMakefile()->GetGeneratorConfigs();
+ for (std::string const& c : configs) {
+ target->GetSourceFiles(sources, c);
+ }
+ auto const sourcesEnd = cmRemoveDuplicates(sources);
+ for (cmSourceFile* sf : cmMakeRange(sources.cbegin(), sourcesEnd)) {
+ Json::Value& lj_source = lj_sources.append(Json::objectValue);
+ std::string const& sfp = sf->ResolveFullPath();
+ fout << sfp << "\n";
+ lj_source["file"] = sfp;
+ if (const char* svalue = sf->GetProperty("LABELS")) {
+ labels.clear();
+ Json::Value& lj_source_labels = lj_source["labels"] = Json::arrayValue;
+ cmExpandList(svalue, labels);
+ for (std::string const& label : labels) {
+ fout << " " << label << "\n";
+ lj_source_labels.append(label);
+ }
+ }
+ }
+ cmGeneratedFileStream json_fout(json_file);
+ json_fout << lj_root;
+ } else
+#endif
+ {
+ cmSystemTools::RemoveFile(file);
+ cmSystemTools::RemoveFile(json_file);
+ }
+}
+
+// static
+std::string cmGlobalGenerator::EscapeJSON(const std::string& s)
+{
+ std::string result;
+ result.reserve(s.size());
+ for (char i : s) {
+ switch (i) {
+ case '"':
+ case '\\':
+ result += '\\';
+ result += i;
+ break;
+ case '\n':
+ result += "\\n";
+ break;
+ case '\t':
+ result += "\\t";
+ break;
+ default:
+ result += i;
+ }
+ }
+ return result;
+}
+
+void cmGlobalGenerator::SetFilenameTargetDepends(
+ cmSourceFile* sf, std::set<cmGeneratorTarget const*> const& tgts)
+{
+ this->FilenameTargetDepends[sf] = tgts;
+}
+
+std::set<cmGeneratorTarget const*> const&
+cmGlobalGenerator::GetFilenameTargetDepends(cmSourceFile* sf) const
+{
+ return this->FilenameTargetDepends[sf];
+}
+
+const std::string& cmGlobalGenerator::GetRealPath(const std::string& dir)
+{
+ auto i = this->RealPaths.lower_bound(dir);
+ if (i == this->RealPaths.end() ||
+ this->RealPaths.key_comp()(dir, i->first)) {
+ i = this->RealPaths.emplace_hint(i, dir, cmSystemTools::GetRealPath(dir));
+ }
+ return i->second;
+}
+
+void cmGlobalGenerator::ProcessEvaluationFiles()
+{
+ std::vector<std::string> generatedFiles;
+ for (auto& localGen : this->LocalGenerators) {
+ localGen->ProcessEvaluationFiles(generatedFiles);
+ }
+}
+
+std::string cmGlobalGenerator::ExpandCFGIntDir(
+ const std::string& str, const std::string& /*config*/) const
+{
+ return str;
+}
+
+bool cmGlobalGenerator::GenerateCPackPropertiesFile()
+{
+ cmake::InstalledFilesMap const& installedFiles =
+ this->CMakeInstance->GetInstalledFiles();
+
+ const auto& lg = this->LocalGenerators[0];
+ cmMakefile* mf = lg->GetMakefile();
+
+ std::vector<std::string> configs;
+ std::string config = mf->GetConfigurations(configs, false);
+
+ std::string path = cmStrCat(this->CMakeInstance->GetHomeOutputDirectory(),
+ "/CPackProperties.cmake");
+
+ if (!cmSystemTools::FileExists(path) && installedFiles.empty()) {
+ return true;
+ }
+
+ cmGeneratedFileStream file(path);
+ file << "# CPack properties\n";
+
+ for (auto const& i : installedFiles) {
+ cmInstalledFile const& installedFile = i.second;
+
+ cmCPackPropertiesGenerator cpackPropertiesGenerator(
+ lg.get(), installedFile, configs);
+
+ cpackPropertiesGenerator.Generate(file, config, configs);
+ }
+
+ return true;
+}