in Source/cmGlobalGenerator.cxx [531:1002]
void cmGlobalGenerator::EnableLanguage(
std::vector<std::string> const& languages, cmMakefile* mf, bool optional)
{
if (!this->IsMultiConfig() &&
!this->GetCMakeInstance()->GetIsInTryCompile()) {
std::string envBuildType;
if (!mf->GetDefinition("CMAKE_BUILD_TYPE") &&
cmSystemTools::GetEnv("CMAKE_BUILD_TYPE", envBuildType)) {
mf->AddCacheDefinition(
"CMAKE_BUILD_TYPE", envBuildType,
"Choose the type of build. Options include: empty, "
"Debug, Release, RelWithDebInfo, MinSizeRel.",
cmStateEnums::STRING);
}
}
if (languages.empty()) {
cmSystemTools::Error("EnableLanguage must have a lang specified!");
cmSystemTools::SetFatalErrorOccurred();
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::SetFatalErrorOccurred();
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 (!cm::contains(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::SetFatalErrorOccurred();
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__)
cmSystemTools::WindowsVersion windowsVersion =
cmSystemTools::GetWindowsVersion();
auto windowsVersionString = cmStrCat(windowsVersion.dwMajorVersion, '.',
windowsVersion.dwMinorVersion, '.',
windowsVersion.dwBuildNumber);
mf->AddDefinition("CMAKE_HOST_SYSTEM_VERSION", windowsVersionString);
#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::SetFatalErrorOccurred();
return;
}
// Tell the generator about the target system.
std::string system = mf->GetSafeDefinition("CMAKE_SYSTEM_NAME");
if (!this->SetSystemName(system, mf)) {
cmSystemTools::SetFatalErrorOccurred();
return;
}
// Tell the generator about the platform, if any.
std::string platform = mf->GetSafeDefinition("CMAKE_GENERATOR_PLATFORM");
if (!this->SetGeneratorPlatform(platform, mf)) {
cmSystemTools::SetFatalErrorOccurred();
return;
}
// Tell the generator about the toolset, if any.
std::string toolset = mf->GetSafeDefinition("CMAKE_GENERATOR_TOOLSET");
if (!this->SetGeneratorToolset(toolset, false, mf)) {
cmSystemTools::SetFatalErrorOccurred();
return;
}
// Find the native build tool for this generator.
if (!this->FindMakeProgram(mf)) {
return;
}
// One-time includes of user-provided project setup files
mf->GetState()->SetInTopLevelIncludes(true);
std::string includes =
mf->GetSafeDefinition("CMAKE_PROJECT_TOP_LEVEL_INCLUDES");
cmList includesList{ includes };
for (std::string setupFile : includesList) {
// Any relative path without a .cmake extension is checked for valid
// cmake modules. This logic should be consistent with CMake's include()
// command. Otherwise default to checking relative path w.r.t. source
// directory
if (!cmSystemTools::FileIsFullPath(setupFile) &&
!cmHasLiteralSuffix(setupFile, ".cmake")) {
std::string mfile = mf->GetModulesFile(cmStrCat(setupFile, ".cmake"));
if (mfile.empty()) {
cmSystemTools::Error(cmStrCat(
"CMAKE_PROJECT_TOP_LEVEL_INCLUDES module:\n ", setupFile));
mf->GetState()->SetInTopLevelIncludes(false);
return;
}
setupFile = mfile;
}
std::string absSetupFile = cmSystemTools::CollapseFullPath(
setupFile, mf->GetCurrentSourceDirectory());
if (!cmSystemTools::FileExists(absSetupFile)) {
cmSystemTools::Error(
cmStrCat("CMAKE_PROJECT_TOP_LEVEL_INCLUDES file does not exist: ",
setupFile));
mf->GetState()->SetInTopLevelIncludes(false);
return;
}
if (cmSystemTools::FileIsDirectory(absSetupFile)) {
cmSystemTools::Error(
cmStrCat("CMAKE_PROJECT_TOP_LEVEL_INCLUDES file is a directory: ",
setupFile));
mf->GetState()->SetInTopLevelIncludes(false);
return;
}
if (!mf->ReadListFile(absSetupFile)) {
cmSystemTools::Error(
cmStrCat("Failed reading CMAKE_PROJECT_TOP_LEVEL_INCLUDES file: ",
setupFile));
mf->GetState()->SetInTopLevelIncludes(false);
return;
}
}
}
mf->GetState()->SetInTopLevelIncludes(false);
// 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(
cmStrCat("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(
cmStrCat("Could not find cmake module file: ", determineCompiler));
}
if (cmSystemTools::GetFatalErrorOccurred()) {
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->GetPolicyStatus(cmPolicies::CMP0132) == cmPolicies::OLD ||
mf->GetPolicyStatus(cmPolicies::CMP0132) == cmPolicies::WARN) &&
!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");
std::string const& envVar = mf->GetRequiredDefinition(compilerEnv);
std::string const& 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(
cmStrCat("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;
cmValue compilerFile = mf->GetDefinition(compilerName);
if (!cmNonempty(compilerFile) || cmIsNOTFOUND(*compilerFile)) {
noCompiler << "No " << compilerName << " could be found.\n";
} else if ((lang != "RC") && (lang != "ASM_MARMASM") &&
(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."
#ifdef _WIN32
" Perhaps the extension is missing?"
#endif
"\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(
cmStrCat("Could not find cmake module file: ", fpath));
} else if (!mf->ReadListFile(informationFile)) {
cmSystemTools::Error(
cmStrCat("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(
cmStrCat("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
// load linker configuration, if required
if (mf->IsOn(cmStrCat("CMAKE_", lang, "_COMPILER_WORKS")) &&
mf->IsOn(cmStrCat("CMAKE_", lang, "_USE_LINKER_INFORMATION"))) {
std::string langLinkerLoadedVar =
cmStrCat("CMAKE_", lang, "_LINKER_INFORMATION_LOADED");
if (!mf->GetDefinition(langLinkerLoadedVar)) {
fpath = cmStrCat("CMake", lang, "LinkerInformation.cmake");
std::string informationFile = mf->GetModulesFile(fpath);
if (informationFile.empty()) {
informationFile = mf->GetModulesFile(cmStrCat("Internal/", fpath));
}
if (informationFile.empty()) {
cmSystemTools::Error(
cmStrCat("Could not find cmake module file: ", fpath));
} else if (!mf->ReadListFile(informationFile)) {
cmSystemTools::Error(cmStrCat(
"Could not process cmake module file: ", informationFile));
}
}
if (needTestLanguage[lang]) {
if (!this->CMakeInstance->GetIsInTryCompile()) {
std::string testLang =
cmStrCat("Internal/CMakeInspect", lang, "Linker.cmake");
std::string ifpath = mf->GetModulesFile(testLang);
if (!mf->ReadListFile(ifpath)) {
cmSystemTools::Error(
cmStrCat("Could not find cmake module file: ", testLang));
}
}
}
}
// 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::SetFatalErrorOccurred();
}
for (std::string const& lang : cur_languages) {
this->LanguagesInProgress.erase(lang);
}
}