diff options
Diffstat (limited to 'Source/cmCoreTryCompile.cxx')
-rw-r--r-- | Source/cmCoreTryCompile.cxx | 695 |
1 files changed, 695 insertions, 0 deletions
diff --git a/Source/cmCoreTryCompile.cxx b/Source/cmCoreTryCompile.cxx new file mode 100644 index 0000000..e9367b1 --- /dev/null +++ b/Source/cmCoreTryCompile.cxx @@ -0,0 +1,695 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2011 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ +#include "cmCoreTryCompile.h" + +#include "cmAlgorithms.h" +#include "cmExportTryCompileFileGenerator.h" +#include "cmGlobalGenerator.h" +#include "cmOutputConverter.h" +#include "cmake.h" +#include <cmsys/Directory.hxx> + +#include <assert.h> + +static std::string const kCMAKE_C_COMPILER_EXTERNAL_TOOLCHAIN = + "CMAKE_C_COMPILER_EXTERNAL_TOOLCHAIN"; +static std::string const kCMAKE_C_COMPILER_TARGET = "CMAKE_C_COMPILER_TARGET"; +static std::string const kCMAKE_CXX_COMPILER_EXTERNAL_TOOLCHAIN = + "CMAKE_CXX_COMPILER_EXTERNAL_TOOLCHAIN"; +static std::string const kCMAKE_CXX_COMPILER_TARGET = + "CMAKE_CXX_COMPILER_TARGET"; +static std::string const kCMAKE_ENABLE_EXPORTS = "CMAKE_ENABLE_EXPORTS"; +static std::string const kCMAKE_LINK_SEARCH_END_STATIC = + "CMAKE_LINK_SEARCH_END_STATIC"; +static std::string const kCMAKE_LINK_SEARCH_START_STATIC = + "CMAKE_LINK_SEARCH_START_STATIC"; +static std::string const kCMAKE_OSX_ARCHITECTURES = "CMAKE_OSX_ARCHITECTURES"; +static std::string const kCMAKE_OSX_DEPLOYMENT_TARGET = + "CMAKE_OSX_DEPLOYMENT_TARGET"; +static std::string const kCMAKE_OSX_SYSROOT = "CMAKE_OSX_SYSROOT"; +static std::string const kCMAKE_POSITION_INDEPENDENT_CODE = + "CMAKE_POSITION_INDEPENDENT_CODE"; +static std::string const kCMAKE_SYSROOT = "CMAKE_SYSROOT"; +static std::string const kCMAKE_TRY_COMPILE_OSX_ARCHITECTURES = + "CMAKE_TRY_COMPILE_OSX_ARCHITECTURES"; +static std::string const kCMAKE_TRY_COMPILE_PLATFORM_VARIABLES = + "CMAKE_TRY_COMPILE_PLATFORM_VARIABLES"; + +int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv, + bool isTryRun) +{ + this->BinaryDirectory = argv[1].c_str(); + this->OutputFile = ""; + // which signature were we called with ? + this->SrcFileSignature = true; + + cmState::TargetType targetType = cmState::EXECUTABLE; + const char* tt = + this->Makefile->GetDefinition("CMAKE_TRY_COMPILE_TARGET_TYPE"); + if (!isTryRun && tt && *tt) { + if (strcmp(tt, cmState::GetTargetTypeName(cmState::EXECUTABLE)) == 0) { + targetType = cmState::EXECUTABLE; + } else if (strcmp(tt, cmState::GetTargetTypeName( + cmState::STATIC_LIBRARY)) == 0) { + targetType = cmState::STATIC_LIBRARY; + } else { + this->Makefile->IssueMessage( + cmake::FATAL_ERROR, std::string("Invalid value '") + tt + + "' for " + "CMAKE_TRY_COMPILE_TARGET_TYPE. Only " + "'" + + cmState::GetTargetTypeName(cmState::EXECUTABLE) + "' and " + "'" + + cmState::GetTargetTypeName(cmState::STATIC_LIBRARY) + + "' " + "are allowed."); + return -1; + } + } + + const char* sourceDirectory = argv[2].c_str(); + const char* projectName = CM_NULLPTR; + std::string targetName; + std::vector<std::string> cmakeFlags(1, "CMAKE_FLAGS"); // fake argv[0] + std::vector<std::string> compileDefs; + std::string outputVariable; + std::string copyFile; + std::string copyFileError; + std::vector<std::string> targets; + std::string libsToLink = " "; + bool useOldLinkLibs = true; + char targetNameBuf[64]; + bool didOutputVariable = false; + bool didCopyFile = false; + bool didCopyFileError = false; + bool useSources = argv[2] == "SOURCES"; + std::vector<std::string> sources; + + enum Doing + { + DoingNone, + DoingCMakeFlags, + DoingCompileDefinitions, + DoingLinkLibraries, + DoingOutputVariable, + DoingCopyFile, + DoingCopyFileError, + DoingSources + }; + Doing doing = useSources ? DoingSources : DoingNone; + for (size_t i = 3; i < argv.size(); ++i) { + if (argv[i] == "CMAKE_FLAGS") { + doing = DoingCMakeFlags; + } else if (argv[i] == "COMPILE_DEFINITIONS") { + doing = DoingCompileDefinitions; + } else if (argv[i] == "LINK_LIBRARIES") { + doing = DoingLinkLibraries; + useOldLinkLibs = false; + } else if (argv[i] == "OUTPUT_VARIABLE") { + doing = DoingOutputVariable; + didOutputVariable = true; + } else if (argv[i] == "COPY_FILE") { + doing = DoingCopyFile; + didCopyFile = true; + } else if (argv[i] == "COPY_FILE_ERROR") { + doing = DoingCopyFileError; + didCopyFileError = true; + } else if (doing == DoingCMakeFlags) { + cmakeFlags.push_back(argv[i]); + } else if (doing == DoingCompileDefinitions) { + compileDefs.push_back(argv[i]); + } else if (doing == DoingLinkLibraries) { + libsToLink += "\"" + cmSystemTools::TrimWhitespace(argv[i]) + "\" "; + if (cmTarget* tgt = this->Makefile->FindTargetToUse(argv[i])) { + switch (tgt->GetType()) { + case cmState::SHARED_LIBRARY: + case cmState::STATIC_LIBRARY: + case cmState::INTERFACE_LIBRARY: + case cmState::UNKNOWN_LIBRARY: + break; + case cmState::EXECUTABLE: + if (tgt->IsExecutableWithExports()) { + break; + } + default: + this->Makefile->IssueMessage( + cmake::FATAL_ERROR, + "Only libraries may be used as try_compile or try_run IMPORTED " + "LINK_LIBRARIES. Got " + + std::string(tgt->GetName()) + " of " + "type " + + cmState::GetTargetTypeName(tgt->GetType()) + "."); + return -1; + } + if (tgt->IsImported()) { + targets.push_back(argv[i]); + } + } + } else if (doing == DoingOutputVariable) { + outputVariable = argv[i].c_str(); + doing = DoingNone; + } else if (doing == DoingCopyFile) { + copyFile = argv[i].c_str(); + doing = DoingNone; + } else if (doing == DoingCopyFileError) { + copyFileError = argv[i].c_str(); + doing = DoingNone; + } else if (doing == DoingSources) { + sources.push_back(argv[i]); + } else if (i == 3) { + this->SrcFileSignature = false; + projectName = argv[i].c_str(); + } else if (i == 4 && !this->SrcFileSignature) { + targetName = argv[i].c_str(); + } else { + std::ostringstream m; + m << "try_compile given unknown argument \"" << argv[i] << "\"."; + this->Makefile->IssueMessage(cmake::AUTHOR_WARNING, m.str()); + } + } + + if (didCopyFile && copyFile.empty()) { + this->Makefile->IssueMessage(cmake::FATAL_ERROR, + "COPY_FILE must be followed by a file path"); + return -1; + } + + if (didCopyFileError && copyFileError.empty()) { + this->Makefile->IssueMessage( + cmake::FATAL_ERROR, + "COPY_FILE_ERROR must be followed by a variable name"); + return -1; + } + + if (didCopyFileError && !didCopyFile) { + this->Makefile->IssueMessage( + cmake::FATAL_ERROR, "COPY_FILE_ERROR may be used only with COPY_FILE"); + return -1; + } + + if (didOutputVariable && outputVariable.empty()) { + this->Makefile->IssueMessage( + cmake::FATAL_ERROR, + "OUTPUT_VARIABLE must be followed by a variable name"); + return -1; + } + + if (useSources && sources.empty()) { + this->Makefile->IssueMessage( + cmake::FATAL_ERROR, + "SOURCES must be followed by at least one source file"); + return -1; + } + + // compute the binary dir when TRY_COMPILE is called with a src file + // signature + if (this->SrcFileSignature) { + this->BinaryDirectory += cmake::GetCMakeFilesDirectory(); + this->BinaryDirectory += "/CMakeTmp"; + } else { + // only valid for srcfile signatures + if (!compileDefs.empty()) { + this->Makefile->IssueMessage( + cmake::FATAL_ERROR, + "COMPILE_DEFINITIONS specified on a srcdir type TRY_COMPILE"); + return -1; + } + if (!copyFile.empty()) { + this->Makefile->IssueMessage( + cmake::FATAL_ERROR, + "COPY_FILE specified on a srcdir type TRY_COMPILE"); + return -1; + } + } + // make sure the binary directory exists + cmSystemTools::MakeDirectory(this->BinaryDirectory.c_str()); + + // do not allow recursive try Compiles + if (this->BinaryDirectory == this->Makefile->GetHomeOutputDirectory()) { + std::ostringstream e; + e << "Attempt at a recursive or nested TRY_COMPILE in directory\n" + << " " << this->BinaryDirectory << "\n"; + this->Makefile->IssueMessage(cmake::FATAL_ERROR, e.str()); + return -1; + } + + std::string outFileName = this->BinaryDirectory + "/CMakeLists.txt"; + // which signature are we using? If we are using var srcfile bindir + if (this->SrcFileSignature) { + // remove any CMakeCache.txt files so we will have a clean test + std::string ccFile = this->BinaryDirectory + "/CMakeCache.txt"; + cmSystemTools::RemoveFile(ccFile); + + // Choose sources. + if (!useSources) { + sources.push_back(argv[2]); + } + + // Detect languages to enable. + cmGlobalGenerator* gg = this->Makefile->GetGlobalGenerator(); + std::set<std::string> testLangs; + for (std::vector<std::string>::iterator si = sources.begin(); + si != sources.end(); ++si) { + std::string ext = cmSystemTools::GetFilenameLastExtension(*si); + std::string lang = gg->GetLanguageFromExtension(ext.c_str()); + if (!lang.empty()) { + testLangs.insert(lang); + } else { + std::ostringstream err; + err << "Unknown extension \"" << ext << "\" for file\n" + << " " << *si << "\n" + << "try_compile() works only for enabled languages. " + << "Currently these are:\n "; + std::vector<std::string> langs; + gg->GetEnabledLanguages(langs); + err << cmJoin(langs, " "); + err << "\nSee project() command to enable other languages."; + this->Makefile->IssueMessage(cmake::FATAL_ERROR, err.str()); + return -1; + } + } + + std::string const tcConfig = + this->Makefile->GetSafeDefinition("CMAKE_TRY_COMPILE_CONFIGURATION"); + + // we need to create a directory and CMakeLists file etc... + // first create the directories + sourceDirectory = this->BinaryDirectory.c_str(); + + // now create a CMakeLists.txt file in that directory + FILE* fout = cmsys::SystemTools::Fopen(outFileName, "w"); + if (!fout) { + std::ostringstream e; + /* clang-format off */ + e << "Failed to open\n" + << " " << outFileName << "\n" + << cmSystemTools::GetLastSystemError(); + /* clang-format on */ + this->Makefile->IssueMessage(cmake::FATAL_ERROR, e.str()); + return -1; + } + + const char* def = this->Makefile->GetDefinition("CMAKE_MODULE_PATH"); + fprintf(fout, "cmake_minimum_required(VERSION %u.%u.%u.%u)\n", + cmVersion::GetMajorVersion(), cmVersion::GetMinorVersion(), + cmVersion::GetPatchVersion(), cmVersion::GetTweakVersion()); + if (def) { + fprintf(fout, "set(CMAKE_MODULE_PATH \"%s\")\n", def); + } + + std::string projectLangs; + for (std::set<std::string>::iterator li = testLangs.begin(); + li != testLangs.end(); ++li) { + projectLangs += " " + *li; + std::string rulesOverrideBase = "CMAKE_USER_MAKE_RULES_OVERRIDE"; + std::string rulesOverrideLang = rulesOverrideBase + "_" + *li; + if (const char* rulesOverridePath = + this->Makefile->GetDefinition(rulesOverrideLang)) { + fprintf(fout, "set(%s \"%s\")\n", rulesOverrideLang.c_str(), + rulesOverridePath); + } else if (const char* rulesOverridePath2 = + this->Makefile->GetDefinition(rulesOverrideBase)) { + fprintf(fout, "set(%s \"%s\")\n", rulesOverrideBase.c_str(), + rulesOverridePath2); + } + } + fprintf(fout, "project(CMAKE_TRY_COMPILE%s)\n", projectLangs.c_str()); + fprintf(fout, "set(CMAKE_VERBOSE_MAKEFILE 1)\n"); + for (std::set<std::string>::iterator li = testLangs.begin(); + li != testLangs.end(); ++li) { + std::string langFlags = "CMAKE_" + *li + "_FLAGS"; + const char* flags = this->Makefile->GetDefinition(langFlags); + fprintf(fout, "set(CMAKE_%s_FLAGS %s)\n", li->c_str(), + cmOutputConverter::EscapeForCMake(flags ? flags : "").c_str()); + fprintf(fout, "set(CMAKE_%s_FLAGS \"${CMAKE_%s_FLAGS}" + " ${COMPILE_DEFINITIONS}\")\n", + li->c_str(), li->c_str()); + } + switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0066)) { + case cmPolicies::WARN: + if (this->Makefile->PolicyOptionalWarningEnabled( + "CMAKE_POLICY_WARNING_CMP0066")) { + std::ostringstream w; + /* clang-format off */ + w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0066) << "\n" + "For compatibility with older versions of CMake, try_compile " + "is not honoring caller config-specific compiler flags " + "(e.g. CMAKE_C_FLAGS_DEBUG) in the test project." + ; + /* clang-format on */ + this->Makefile->IssueMessage(cmake::AUTHOR_WARNING, w.str()); + } + case cmPolicies::OLD: + // OLD behavior is to do nothing. + break; + case cmPolicies::REQUIRED_IF_USED: + case cmPolicies::REQUIRED_ALWAYS: + this->Makefile->IssueMessage( + cmake::FATAL_ERROR, + cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0066)); + case cmPolicies::NEW: { + // NEW behavior is to pass config-specific compiler flags. + static std::string const cfgDefault = "DEBUG"; + std::string const cfg = + !tcConfig.empty() ? cmSystemTools::UpperCase(tcConfig) : cfgDefault; + for (std::set<std::string>::iterator li = testLangs.begin(); + li != testLangs.end(); ++li) { + std::string const langFlagsCfg = "CMAKE_" + *li + "_FLAGS_" + cfg; + const char* flagsCfg = this->Makefile->GetDefinition(langFlagsCfg); + fprintf(fout, "set(%s %s)\n", langFlagsCfg.c_str(), + cmOutputConverter::EscapeForCMake(flagsCfg ? flagsCfg : "") + .c_str()); + } + } break; + } + switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0056)) { + case cmPolicies::WARN: + if (this->Makefile->PolicyOptionalWarningEnabled( + "CMAKE_POLICY_WARNING_CMP0056")) { + std::ostringstream w; + /* clang-format off */ + w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0056) << "\n" + "For compatibility with older versions of CMake, try_compile " + "is not honoring caller link flags (e.g. CMAKE_EXE_LINKER_FLAGS) " + "in the test project." + ; + /* clang-format on */ + this->Makefile->IssueMessage(cmake::AUTHOR_WARNING, w.str()); + } + case cmPolicies::OLD: + // OLD behavior is to do nothing. + break; + case cmPolicies::REQUIRED_IF_USED: + case cmPolicies::REQUIRED_ALWAYS: + this->Makefile->IssueMessage( + cmake::FATAL_ERROR, + cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0056)); + case cmPolicies::NEW: + // NEW behavior is to pass linker flags. + { + const char* exeLinkFlags = + this->Makefile->GetDefinition("CMAKE_EXE_LINKER_FLAGS"); + fprintf( + fout, "set(CMAKE_EXE_LINKER_FLAGS %s)\n", + cmOutputConverter::EscapeForCMake(exeLinkFlags ? exeLinkFlags : "") + .c_str()); + } + break; + } + fprintf(fout, "set(CMAKE_EXE_LINKER_FLAGS \"${CMAKE_EXE_LINKER_FLAGS}" + " ${EXE_LINKER_FLAGS}\")\n"); + fprintf(fout, "include_directories(${INCLUDE_DIRECTORIES})\n"); + fprintf(fout, "set(CMAKE_SUPPRESS_REGENERATION 1)\n"); + fprintf(fout, "link_directories(${LINK_DIRECTORIES})\n"); + // handle any compile flags we need to pass on + if (!compileDefs.empty()) { + fprintf(fout, "add_definitions(%s)\n", cmJoin(compileDefs, " ").c_str()); + } + + /* Use a random file name to avoid rapid creation and deletion + of the same executable name (some filesystems fail on that). */ + sprintf(targetNameBuf, "cmTC_%05x", cmSystemTools::RandomSeed() & 0xFFFFF); + targetName = targetNameBuf; + + if (!targets.empty()) { + std::string fname = "/" + std::string(targetName) + "Targets.cmake"; + cmExportTryCompileFileGenerator tcfg(gg, targets, this->Makefile); + tcfg.SetExportFile((this->BinaryDirectory + fname).c_str()); + tcfg.SetConfig(tcConfig); + + if (!tcfg.GenerateImportFile()) { + this->Makefile->IssueMessage(cmake::FATAL_ERROR, + "could not write export file."); + fclose(fout); + return -1; + } + fprintf(fout, "\ninclude(\"${CMAKE_CURRENT_LIST_DIR}/%s\")\n\n", + fname.c_str()); + } + + // Forward a set of variables to the inner project cache. + { + std::set<std::string> vars; + vars.insert(kCMAKE_C_COMPILER_EXTERNAL_TOOLCHAIN); + vars.insert(kCMAKE_C_COMPILER_TARGET); + vars.insert(kCMAKE_CXX_COMPILER_EXTERNAL_TOOLCHAIN); + vars.insert(kCMAKE_CXX_COMPILER_TARGET); + vars.insert(kCMAKE_ENABLE_EXPORTS); + vars.insert(kCMAKE_LINK_SEARCH_END_STATIC); + vars.insert(kCMAKE_LINK_SEARCH_START_STATIC); + vars.insert(kCMAKE_OSX_ARCHITECTURES); + vars.insert(kCMAKE_OSX_DEPLOYMENT_TARGET); + vars.insert(kCMAKE_OSX_SYSROOT); + vars.insert(kCMAKE_POSITION_INDEPENDENT_CODE); + vars.insert(kCMAKE_SYSROOT); + + if (const char* varListStr = this->Makefile->GetDefinition( + kCMAKE_TRY_COMPILE_PLATFORM_VARIABLES)) { + std::vector<std::string> varList; + cmSystemTools::ExpandListArgument(varListStr, varList); + vars.insert(varList.begin(), varList.end()); + } + + /* for the TRY_COMPILEs we want to be able to specify the architecture. + So the user can set CMAKE_OSX_ARCHITECTURES to i386;ppc and then set + CMAKE_TRY_COMPILE_OSX_ARCHITECTURES first to i386 and then to ppc to + have the tests run for each specific architecture. Since + cmLocalGenerator doesn't allow building for "the other" + architecture only via CMAKE_OSX_ARCHITECTURES. + */ + if (const char* tcArchs = this->Makefile->GetDefinition( + kCMAKE_TRY_COMPILE_OSX_ARCHITECTURES)) { + vars.erase(kCMAKE_OSX_ARCHITECTURES); + std::string flag = "-DCMAKE_OSX_ARCHITECTURES=" + std::string(tcArchs); + cmakeFlags.push_back(flag); + } + + for (std::set<std::string>::iterator vi = vars.begin(); vi != vars.end(); + ++vi) { + std::string const& var = *vi; + if (const char* val = this->Makefile->GetDefinition(var)) { + std::string flag = "-D" + var + "=" + val; + cmakeFlags.push_back(flag); + } + } + } + + /* Set the appropriate policy information for ENABLE_EXPORTS */ + fprintf(fout, "cmake_policy(SET CMP0065 %s)\n", + this->Makefile->GetPolicyStatus(cmPolicies::CMP0065) == + cmPolicies::NEW + ? "NEW" + : "OLD"); + + if (targetType == cmState::EXECUTABLE) { + /* Put the executable at a known location (for COPY_FILE). */ + fprintf(fout, "set(CMAKE_RUNTIME_OUTPUT_DIRECTORY \"%s\")\n", + this->BinaryDirectory.c_str()); + /* Create the actual executable. */ + fprintf(fout, "add_executable(%s", targetName.c_str()); + } else // if (targetType == cmState::STATIC_LIBRARY) + { + /* Put the static library at a known location (for COPY_FILE). */ + fprintf(fout, "set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY \"%s\")\n", + this->BinaryDirectory.c_str()); + /* Create the actual static library. */ + fprintf(fout, "add_library(%s STATIC", targetName.c_str()); + } + for (std::vector<std::string>::iterator si = sources.begin(); + si != sources.end(); ++si) { + fprintf(fout, " \"%s\"", si->c_str()); + + // Add dependencies on any non-temporary sources. + if (si->find("CMakeTmp") == si->npos) { + this->Makefile->AddCMakeDependFile(*si); + } + } + fprintf(fout, ")\n"); + if (useOldLinkLibs) { + fprintf(fout, "target_link_libraries(%s ${LINK_LIBRARIES})\n", + targetName.c_str()); + } else { + fprintf(fout, "target_link_libraries(%s %s)\n", targetName.c_str(), + libsToLink.c_str()); + } + fclose(fout); + projectName = "CMAKE_TRY_COMPILE"; + } + + bool erroroc = cmSystemTools::GetErrorOccuredFlag(); + cmSystemTools::ResetErrorOccuredFlag(); + std::string output; + // actually do the try compile now that everything is setup + int res = this->Makefile->TryCompile( + sourceDirectory, this->BinaryDirectory, projectName, targetName, + this->SrcFileSignature, &cmakeFlags, output); + if (erroroc) { + cmSystemTools::SetErrorOccured(); + } + + // set the result var to the return value to indicate success or failure + this->Makefile->AddCacheDefinition(argv[0], (res == 0 ? "TRUE" : "FALSE"), + "Result of TRY_COMPILE", + cmState::INTERNAL); + + if (!outputVariable.empty()) { + this->Makefile->AddDefinition(outputVariable, output.c_str()); + } + + if (this->SrcFileSignature) { + std::string copyFileErrorMessage; + this->FindOutputFile(targetName, targetType); + + if ((res == 0) && !copyFile.empty()) { + if (this->OutputFile.empty() || + !cmSystemTools::CopyFileAlways(this->OutputFile, copyFile)) { + std::ostringstream emsg; + /* clang-format off */ + emsg << "Cannot copy output executable\n" + << " '" << this->OutputFile << "'\n" + << "to destination specified by COPY_FILE:\n" + << " '" << copyFile << "'\n"; + /* clang-format on */ + if (!this->FindErrorMessage.empty()) { + emsg << this->FindErrorMessage.c_str(); + } + if (copyFileError.empty()) { + this->Makefile->IssueMessage(cmake::FATAL_ERROR, emsg.str()); + return -1; + } else { + copyFileErrorMessage = emsg.str(); + } + } + } + + if (!copyFileError.empty()) { + this->Makefile->AddDefinition(copyFileError, + copyFileErrorMessage.c_str()); + } + } + return res; +} + +void cmCoreTryCompile::CleanupFiles(const char* binDir) +{ + if (!binDir) { + return; + } + + std::string bdir = binDir; + if (bdir.find("CMakeTmp") == std::string::npos) { + cmSystemTools::Error( + "TRY_COMPILE attempt to remove -rf directory that does not contain " + "CMakeTmp:", + binDir); + return; + } + + cmsys::Directory dir; + dir.Load(binDir); + size_t fileNum; + std::set<std::string> deletedFiles; + for (fileNum = 0; fileNum < dir.GetNumberOfFiles(); ++fileNum) { + if (strcmp(dir.GetFile(static_cast<unsigned long>(fileNum)), ".") && + strcmp(dir.GetFile(static_cast<unsigned long>(fileNum)), "..")) { + + if (deletedFiles.find(dir.GetFile( + static_cast<unsigned long>(fileNum))) == deletedFiles.end()) { + deletedFiles.insert(dir.GetFile(static_cast<unsigned long>(fileNum))); + std::string fullPath = binDir; + fullPath += "/"; + fullPath += dir.GetFile(static_cast<unsigned long>(fileNum)); + if (cmSystemTools::FileIsDirectory(fullPath)) { + this->CleanupFiles(fullPath.c_str()); + cmSystemTools::RemoveADirectory(fullPath); + } else { +#ifdef _WIN32 + // Sometimes anti-virus software hangs on to new files so we + // cannot delete them immediately. Try a few times. + cmSystemTools::WindowsFileRetry retry = + cmSystemTools::GetWindowsFileRetry(); + while (!cmSystemTools::RemoveFile(fullPath.c_str()) && + --retry.Count && + cmSystemTools::FileExists(fullPath.c_str())) { + cmSystemTools::Delay(retry.Delay); + } + if (retry.Count == 0) +#else + if (!cmSystemTools::RemoveFile(fullPath)) +#endif + { + std::string m = "Remove failed on file: " + fullPath; + cmSystemTools::ReportLastSystemError(m.c_str()); + } + } + } + } + } +} + +void cmCoreTryCompile::FindOutputFile(const std::string& targetName, + cmState::TargetType targetType) +{ + this->FindErrorMessage = ""; + this->OutputFile = ""; + std::string tmpOutputFile = "/"; + if (targetType == cmState::EXECUTABLE) { + tmpOutputFile += targetName; + tmpOutputFile += + this->Makefile->GetSafeDefinition("CMAKE_EXECUTABLE_SUFFIX"); + } else // if (targetType == cmState::STATIC_LIBRARY) + { + tmpOutputFile += + this->Makefile->GetSafeDefinition("CMAKE_STATIC_LIBRARY_PREFIX"); + tmpOutputFile += targetName; + tmpOutputFile += + this->Makefile->GetSafeDefinition("CMAKE_STATIC_LIBRARY_SUFFIX"); + } + + // a list of directories where to search for the compilation result + // at first directly in the binary dir + std::vector<std::string> searchDirs; + searchDirs.push_back(""); + + const char* config = + this->Makefile->GetDefinition("CMAKE_TRY_COMPILE_CONFIGURATION"); + // if a config was specified try that first + if (config && config[0]) { + std::string tmp = "/"; + tmp += config; + searchDirs.push_back(tmp); + } + searchDirs.push_back("/Debug"); +#if defined(__APPLE__) + std::string app = "/Debug/" + targetName + ".app"; + searchDirs.push_back(app); +#endif + searchDirs.push_back("/Development"); + + for (std::vector<std::string>::const_iterator it = searchDirs.begin(); + it != searchDirs.end(); ++it) { + std::string command = this->BinaryDirectory; + command += *it; + command += tmpOutputFile; + if (cmSystemTools::FileExists(command.c_str())) { + this->OutputFile = cmSystemTools::CollapseFullPath(command); + return; + } + } + + std::ostringstream emsg; + emsg << "Unable to find the executable at any of:\n"; + emsg << cmWrap(" " + this->BinaryDirectory, searchDirs, tmpOutputFile, "\n") + << "\n"; + this->FindErrorMessage = emsg.str(); + return; +} |