cmCoreTryCompile.cxx 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376
  1. /*=========================================================================
  2. Program: CMake - Cross-Platform Makefile Generator
  3. Module: $RCSfile$
  4. Language: C++
  5. Date: $Date$
  6. Version: $Revision$
  7. Copyright (c) 2007 Kitware, Inc., Insight Consortium. All rights reserved.
  8. See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.
  9. This software is distributed WITHOUT ANY WARRANTY; without even
  10. the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
  11. PURPOSE. See the above copyright notices for more information.
  12. =========================================================================*/
  13. #include "cmCoreTryCompile.h"
  14. #include "cmake.h"
  15. #include "cmCacheManager.h"
  16. #include "cmGlobalGenerator.h"
  17. #include <cmsys/Directory.hxx>
  18. int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv)
  19. {
  20. this->BinaryDirectory = argv[1].c_str();
  21. this->OutputFile = "";
  22. // which signature were we called with ?
  23. this->SrcFileSignature = false;
  24. unsigned int i;
  25. const char* sourceDirectory = argv[2].c_str();
  26. const char* projectName = 0;
  27. const char* targetName = 0;
  28. int extraArgs = 0;
  29. // look for CMAKE_FLAGS and store them
  30. std::vector<std::string> cmakeFlags;
  31. for (i = 3; i < argv.size(); ++i)
  32. {
  33. if (argv[i] == "CMAKE_FLAGS")
  34. {
  35. // CMAKE_FLAGS is the first argument because we need an argv[0] that
  36. // is not used, so it matches regular command line parsing which has
  37. // the program name as arg 0
  38. for (; i < argv.size() && argv[i] != "COMPILE_DEFINITIONS" &&
  39. argv[i] != "OUTPUT_VARIABLE";
  40. ++i)
  41. {
  42. extraArgs++;
  43. cmakeFlags.push_back(argv[i]);
  44. }
  45. break;
  46. }
  47. }
  48. // look for OUTPUT_VARIABLE and store them
  49. std::string outputVariable;
  50. for (i = 3; i < argv.size(); ++i)
  51. {
  52. if (argv[i] == "OUTPUT_VARIABLE")
  53. {
  54. if ( argv.size() <= (i+1) )
  55. {
  56. cmSystemTools::Error(
  57. "OUTPUT_VARIABLE specified but there is no variable");
  58. return -1;
  59. }
  60. extraArgs += 2;
  61. outputVariable = argv[i+1];
  62. break;
  63. }
  64. }
  65. // look for COMPILE_DEFINITIONS and store them
  66. std::vector<std::string> compileFlags;
  67. for (i = 3; i < argv.size(); ++i)
  68. {
  69. if (argv[i] == "COMPILE_DEFINITIONS")
  70. {
  71. extraArgs++;
  72. for (i = i + 1; i < argv.size() && argv[i] != "CMAKE_FLAGS" &&
  73. argv[i] != "OUTPUT_VARIABLE";
  74. ++i)
  75. {
  76. extraArgs++;
  77. compileFlags.push_back(argv[i]);
  78. }
  79. break;
  80. }
  81. }
  82. // do we have a srcfile signature
  83. if (argv.size() - extraArgs == 3)
  84. {
  85. this->SrcFileSignature = true;
  86. }
  87. // compute the binary dir when TRY_COMPILE is called with a src file
  88. // signature
  89. if (this->SrcFileSignature)
  90. {
  91. this->BinaryDirectory += cmake::GetCMakeFilesDirectory();
  92. this->BinaryDirectory += "/CMakeTmp";
  93. }
  94. else
  95. {
  96. // only valid for srcfile signatures
  97. if (compileFlags.size())
  98. {
  99. cmSystemTools::Error(
  100. "COMPILE_FLAGS specified on a srcdir type TRY_COMPILE");
  101. return -1;
  102. }
  103. }
  104. // make sure the binary directory exists
  105. cmSystemTools::MakeDirectory(this->BinaryDirectory.c_str());
  106. // do not allow recursive try Compiles
  107. if (this->BinaryDirectory == this->Makefile->GetHomeOutputDirectory())
  108. {
  109. cmSystemTools::Error(
  110. "Attempt at a recursive or nested TRY_COMPILE in directory ",
  111. this->BinaryDirectory.c_str());
  112. return -1;
  113. }
  114. std::string outFileName = this->BinaryDirectory + "/CMakeLists.txt";
  115. // which signature are we using? If we are using var srcfile bindir
  116. if (this->SrcFileSignature)
  117. {
  118. // remove any CMakeCache.txt files so we will have a clean test
  119. std::string ccFile = this->BinaryDirectory + "/CMakeCache.txt";
  120. cmSystemTools::RemoveFile(ccFile.c_str());
  121. // we need to create a directory and CMakeList file etc...
  122. // first create the directories
  123. sourceDirectory = this->BinaryDirectory.c_str();
  124. // now create a CMakeList.txt file in that directory
  125. FILE *fout = fopen(outFileName.c_str(),"w");
  126. if (!fout)
  127. {
  128. cmSystemTools::Error("Failed to create CMakeList file for ",
  129. outFileName.c_str());
  130. cmSystemTools::ReportLastSystemError("");
  131. return -1;
  132. }
  133. std::string source = argv[2];
  134. std::string ext = cmSystemTools::GetFilenameExtension(source);
  135. const char* lang =(this->Makefile->GetCMakeInstance()->GetGlobalGenerator()
  136. ->GetLanguageFromExtension(ext.c_str()));
  137. const char* def = this->Makefile->GetDefinition("CMAKE_MODULE_PATH");
  138. if(def)
  139. {
  140. fprintf(fout, "SET(CMAKE_MODULE_PATH %s)\n", def);
  141. }
  142. if(lang)
  143. {
  144. fprintf(fout, "PROJECT(CMAKE_TRY_COMPILE %s)\n", lang);
  145. }
  146. else
  147. {
  148. cmOStringStream err;
  149. err << "Unknown extension \"" << ext << "\" for file \""
  150. << source << "\". TRY_COMPILE only works for enabled languages.\n"
  151. << "Currently enabled languages are:";
  152. std::vector<std::string> langs;
  153. this->Makefile->GetCMakeInstance()->GetGlobalGenerator()->
  154. GetEnabledLanguages(langs);
  155. for(std::vector<std::string>::iterator l = langs.begin();
  156. l != langs.end(); ++l)
  157. {
  158. err << " " << *l;
  159. }
  160. err << "\nSee PROJECT command for help enabling other languages.";
  161. cmSystemTools::Error(err.str().c_str());
  162. fclose(fout);
  163. return -1;
  164. }
  165. std::string langFlags = "CMAKE_";
  166. langFlags += lang;
  167. langFlags += "_FLAGS";
  168. fprintf(fout, "SET(CMAKE_VERBOSE_MAKEFILE 1)\n");
  169. fprintf(fout, "SET(CMAKE_%s_FLAGS \"", lang);
  170. const char* flags = this->Makefile->GetDefinition(langFlags.c_str());
  171. if(flags)
  172. {
  173. fprintf(fout, " %s ", flags);
  174. }
  175. fprintf(fout, " ${COMPILE_DEFINITIONS}\")\n");
  176. fprintf(fout, "INCLUDE_DIRECTORIES(${INCLUDE_DIRECTORIES})\n");
  177. fprintf(fout, "LINK_DIRECTORIES(${LINK_DIRECTORIES})\n");
  178. // handle any compile flags we need to pass on
  179. if (compileFlags.size())
  180. {
  181. fprintf(fout, "ADD_DEFINITIONS( ");
  182. for (i = 0; i < compileFlags.size(); ++i)
  183. {
  184. fprintf(fout,"%s ",compileFlags[i].c_str());
  185. }
  186. fprintf(fout, ")\n");
  187. }
  188. const char* platformOptions =
  189. this->Makefile->GetDefinition("CMAKE_TRY_COMPILE_PLATFORM_OPTIONS");
  190. if ( platformOptions )
  191. {
  192. fprintf(fout, "%s\n", platformOptions);
  193. }
  194. fprintf(fout, "ADD_EXECUTABLE(cmTryCompileExec \"%s\")\n",source.c_str());
  195. fprintf(fout,
  196. "TARGET_LINK_LIBRARIES(cmTryCompileExec ${LINK_LIBRARIES})\n");
  197. fclose(fout);
  198. projectName = "CMAKE_TRY_COMPILE";
  199. targetName = "cmTryCompileExec";
  200. // if the source is not in CMakeTmp
  201. if(source.find("CMakeTmp") == source.npos)
  202. {
  203. this->Makefile->AddCMakeDependFile(source.c_str());
  204. }
  205. }
  206. // else the srcdir bindir project target signature
  207. else
  208. {
  209. projectName = argv[3].c_str();
  210. if (argv.size() - extraArgs == 5)
  211. {
  212. targetName = argv[4].c_str();
  213. }
  214. }
  215. bool erroroc = cmSystemTools::GetErrorOccuredFlag();
  216. cmSystemTools::ResetErrorOccuredFlag();
  217. std::string output;
  218. // actually do the try compile now that everything is setup
  219. int res = this->Makefile->TryCompile(sourceDirectory,
  220. this->BinaryDirectory.c_str(),
  221. projectName,
  222. targetName,
  223. &cmakeFlags,
  224. &output);
  225. if ( erroroc )
  226. {
  227. cmSystemTools::SetErrorOccured();
  228. }
  229. // set the result var to the return value to indicate success or failure
  230. this->Makefile->AddCacheDefinition(argv[0].c_str(),
  231. (res == 0 ? "TRUE" : "FALSE"),
  232. "Result of TRY_COMPILE",
  233. cmCacheManager::INTERNAL);
  234. if ( outputVariable.size() > 0 )
  235. {
  236. this->Makefile->AddDefinition(outputVariable.c_str(), output.c_str());
  237. }
  238. if (this->SrcFileSignature)
  239. {
  240. this->FindOutputFile(targetName);
  241. }
  242. return res;
  243. }
  244. void cmCoreTryCompile::CleanupFiles(const char* binDir)
  245. {
  246. if ( !binDir )
  247. {
  248. return;
  249. }
  250. std::string bdir = binDir;
  251. if(bdir.find("CMakeTmp") == std::string::npos)
  252. {
  253. cmSystemTools::Error(
  254. "TRY_COMPILE attempt to remove -rf directory that does not contain "
  255. "CMakeTmp:", binDir);
  256. return;
  257. }
  258. cmsys::Directory dir;
  259. dir.Load(binDir);
  260. size_t fileNum;
  261. std::set<cmStdString> deletedFiles;
  262. for (fileNum = 0; fileNum < dir.GetNumberOfFiles(); ++fileNum)
  263. {
  264. if (strcmp(dir.GetFile(static_cast<unsigned long>(fileNum)),".") &&
  265. strcmp(dir.GetFile(static_cast<unsigned long>(fileNum)),".."))
  266. {
  267. if(deletedFiles.find( dir.GetFile(static_cast<unsigned long>(fileNum)))
  268. == deletedFiles.end())
  269. {
  270. deletedFiles.insert(dir.GetFile(static_cast<unsigned long>(fileNum)));
  271. std::string fullPath = binDir;
  272. fullPath += "/";
  273. fullPath += dir.GetFile(static_cast<unsigned long>(fileNum));
  274. if(cmSystemTools::FileIsDirectory(fullPath.c_str()))
  275. {
  276. this->CleanupFiles(fullPath.c_str());
  277. }
  278. else
  279. {
  280. if(!cmSystemTools::RemoveFile(fullPath.c_str()))
  281. {
  282. std::string m = "Remove failed on file: ";
  283. m += fullPath;
  284. cmSystemTools::ReportLastSystemError(m.c_str());
  285. }
  286. }
  287. }
  288. }
  289. }
  290. }
  291. void cmCoreTryCompile::FindOutputFile(const char* targetName)
  292. {
  293. this->FindErrorMessage = "";
  294. this->OutputFile = "";
  295. std::string tmpOutputFile = "/";
  296. tmpOutputFile += targetName;
  297. tmpOutputFile +=this->Makefile->GetSafeDefinition("CMAKE_EXECUTABLE_SUFFIX");
  298. // a list of directories where to search for the compilation result
  299. // at first directly in the binary dir
  300. std::vector<std::string> searchDirs;
  301. searchDirs.push_back("");
  302. const char* config = this->Makefile->GetDefinition(
  303. "CMAKE_TRY_COMPILE_CONFIGURATION");
  304. // if a config was specified try that first
  305. if (config && config[0])
  306. {
  307. std::string tmp = "/";
  308. tmp += config;
  309. searchDirs.push_back(tmp);
  310. }
  311. searchDirs.push_back("/Debug");
  312. searchDirs.push_back("/Development");
  313. for(std::vector<std::string>::const_iterator it = searchDirs.begin();
  314. it != searchDirs.end();
  315. ++it)
  316. {
  317. std::string command = this->BinaryDirectory;
  318. command += *it;
  319. command += tmpOutputFile;
  320. if(cmSystemTools::FileExists(command.c_str()))
  321. {
  322. tmpOutputFile = cmSystemTools::CollapseFullPath(command.c_str());
  323. this->OutputFile = tmpOutputFile;
  324. return;
  325. }
  326. }
  327. cmOStringStream emsg;
  328. emsg << "Unable to find executable for " << this->GetName() << ": tried \"";
  329. for (unsigned int i = 0; i < searchDirs.size(); ++i)
  330. {
  331. emsg << this->BinaryDirectory << searchDirs[i] << tmpOutputFile;
  332. if (i < searchDirs.size() - 1)
  333. {
  334. emsg << "\" and \"";
  335. }
  336. else
  337. {
  338. emsg << "\".";
  339. }
  340. }
  341. this->FindErrorMessage = emsg.str();
  342. return;
  343. }