1 /*============================================================================
2 CMake - Cross Platform Makefile Generator
3 Copyright 2000-2011 Kitware, Inc., Insight Software Consortium
5 Distributed under the OSI-approved BSD License (the "License");
6 see accompanying file Copyright.txt for details.
8 This software is distributed WITHOUT ANY WARRANTY; without even the
9 implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10 See the License for more information.
11 ============================================================================*/
12 #include "cmCoreTryCompile.h"
14 #include "cmCacheManager.h"
15 #include "cmGlobalGenerator.h"
16 #include <cmsys/Directory.hxx>
18 int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv)
20 this->BinaryDirectory = argv[1].c_str();
21 this->OutputFile = "";
22 // which signature were we called with ?
23 this->SrcFileSignature = false;
26 const char* sourceDirectory = argv[2].c_str();
27 const char* projectName = 0;
28 const char* targetName = 0;
29 char targetNameBuf[64];
32 // look for CMAKE_FLAGS and store them
33 std::vector<std::string> cmakeFlags;
34 for (i = 3; i < argv.size(); ++i)
36 if (argv[i] == "CMAKE_FLAGS")
38 // CMAKE_FLAGS is the first argument because we need an argv[0] that
39 // is not used, so it matches regular command line parsing which has
40 // the program name as arg 0
41 for (; i < argv.size() && argv[i] != "COMPILE_DEFINITIONS" &&
42 argv[i] != "OUTPUT_VARIABLE";
46 cmakeFlags.push_back(argv[i]);
52 // look for OUTPUT_VARIABLE and store them
53 std::string outputVariable;
54 for (i = 3; i < argv.size(); ++i)
56 if (argv[i] == "OUTPUT_VARIABLE")
58 if ( argv.size() <= (i+1) )
60 this->Makefile->IssueMessage(cmake::FATAL_ERROR,
61 "OUTPUT_VARIABLE specified but there is no variable");
65 outputVariable = argv[i+1];
70 // look for COMPILE_DEFINITIONS and store them
71 std::vector<std::string> compileFlags;
72 for (i = 3; i < argv.size(); ++i)
74 if (argv[i] == "COMPILE_DEFINITIONS")
77 for (i = i + 1; i < argv.size() && argv[i] != "CMAKE_FLAGS" &&
78 argv[i] != "OUTPUT_VARIABLE";
82 compileFlags.push_back(argv[i]);
90 for (i = 3; i < argv.size(); ++i)
92 if (argv[i] == "COPY_FILE")
94 if ( argv.size() <= (i+1) )
96 this->Makefile->IssueMessage(cmake::FATAL_ERROR,
97 "COPY_FILE specified but there is no variable");
101 copyFile = argv[i+1];
106 // do we have a srcfile signature
107 if (argv.size() - extraArgs == 3)
109 this->SrcFileSignature = true;
112 // compute the binary dir when TRY_COMPILE is called with a src file
114 if (this->SrcFileSignature)
116 this->BinaryDirectory += cmake::GetCMakeFilesDirectory();
117 this->BinaryDirectory += "/CMakeTmp";
121 // only valid for srcfile signatures
122 if (compileFlags.size())
124 this->Makefile->IssueMessage(cmake::FATAL_ERROR,
125 "COMPILE_FLAGS specified on a srcdir type TRY_COMPILE");
130 this->Makefile->IssueMessage(cmake::FATAL_ERROR,
131 "COPY_FILE specified on a srcdir type TRY_COMPILE");
135 // make sure the binary directory exists
136 cmSystemTools::MakeDirectory(this->BinaryDirectory.c_str());
138 // do not allow recursive try Compiles
139 if (this->BinaryDirectory == this->Makefile->GetHomeOutputDirectory())
142 e << "Attempt at a recursive or nested TRY_COMPILE in directory\n"
143 << " " << this->BinaryDirectory << "\n";
144 this->Makefile->IssueMessage(cmake::FATAL_ERROR, e.str());
148 std::string outFileName = this->BinaryDirectory + "/CMakeLists.txt";
149 // which signature are we using? If we are using var srcfile bindir
150 if (this->SrcFileSignature)
152 // remove any CMakeCache.txt files so we will have a clean test
153 std::string ccFile = this->BinaryDirectory + "/CMakeCache.txt";
154 cmSystemTools::RemoveFile(ccFile.c_str());
156 // we need to create a directory and CMakeLists file etc...
157 // first create the directories
158 sourceDirectory = this->BinaryDirectory.c_str();
160 // now create a CMakeLists.txt file in that directory
161 FILE *fout = fopen(outFileName.c_str(),"w");
165 e << "Failed to open\n"
166 << " " << outFileName.c_str() << "\n"
167 << cmSystemTools::GetLastSystemError();
168 this->Makefile->IssueMessage(cmake::FATAL_ERROR, e.str());
172 std::string source = argv[2];
173 std::string ext = cmSystemTools::GetFilenameLastExtension(source);
174 const char* lang =(this->Makefile->GetCMakeInstance()->GetGlobalGenerator()
175 ->GetLanguageFromExtension(ext.c_str()));
176 const char* def = this->Makefile->GetDefinition("CMAKE_MODULE_PATH");
177 fprintf(fout, "cmake_minimum_required(VERSION %u.%u.%u.%u)\n",
178 cmVersion::GetMajorVersion(), cmVersion::GetMinorVersion(),
179 cmVersion::GetPatchVersion(), cmVersion::GetTweakVersion());
182 fprintf(fout, "SET(CMAKE_MODULE_PATH %s)\n", def);
185 const char* rulesOverrideBase = "CMAKE_USER_MAKE_RULES_OVERRIDE";
186 std::string rulesOverrideLang =
187 rulesOverrideBase + (lang ? std::string("_") + lang : std::string(""));
188 if(const char* rulesOverridePath =
189 this->Makefile->GetDefinition(rulesOverrideLang.c_str()))
191 fprintf(fout, "SET(%s \"%s\")\n",
192 rulesOverrideLang.c_str(), rulesOverridePath);
194 else if(const char* rulesOverridePath2 =
195 this->Makefile->GetDefinition(rulesOverrideBase))
197 fprintf(fout, "SET(%s \"%s\")\n",
198 rulesOverrideBase, rulesOverridePath2);
203 fprintf(fout, "PROJECT(CMAKE_TRY_COMPILE %s)\n", lang);
209 err << "Unknown extension \"" << ext << "\" for file\n"
210 << " " << source << "\n"
211 << "try_compile() works only for enabled languages. "
212 << "Currently these are:\n ";
213 std::vector<std::string> langs;
214 this->Makefile->GetCMakeInstance()->GetGlobalGenerator()->
215 GetEnabledLanguages(langs);
216 for(std::vector<std::string>::iterator l = langs.begin();
217 l != langs.end(); ++l)
221 err << "\nSee project() command to enable other languages.";
222 this->Makefile->IssueMessage(cmake::FATAL_ERROR, err.str());
225 std::string langFlags = "CMAKE_";
227 langFlags += "_FLAGS";
228 fprintf(fout, "SET(CMAKE_VERBOSE_MAKEFILE 1)\n");
229 fprintf(fout, "SET(CMAKE_%s_FLAGS \"", lang);
230 const char* flags = this->Makefile->GetDefinition(langFlags.c_str());
233 fprintf(fout, " %s ", flags);
235 fprintf(fout, " ${COMPILE_DEFINITIONS}\")\n");
236 fprintf(fout, "INCLUDE_DIRECTORIES(${INCLUDE_DIRECTORIES})\n");
237 fprintf(fout, "SET(CMAKE_SUPPRESS_REGENERATION 1)\n");
238 fprintf(fout, "LINK_DIRECTORIES(${LINK_DIRECTORIES})\n");
239 // handle any compile flags we need to pass on
240 if (compileFlags.size())
242 fprintf(fout, "ADD_DEFINITIONS( ");
243 for (i = 0; i < compileFlags.size(); ++i)
245 fprintf(fout,"%s ",compileFlags[i].c_str());
247 fprintf(fout, ")\n");
250 /* for the TRY_COMPILEs we want to be able to specify the architecture.
251 So the user can set CMAKE_OSX_ARCHITECTURE to i386;ppc and then set
252 CMAKE_TRY_COMPILE_OSX_ARCHITECTURE first to i386 and then to ppc to
253 have the tests run for each specific architecture. Since
254 cmLocalGenerator doesn't allow building for "the other"
255 architecture only via CMAKE_OSX_ARCHITECTURES.
257 if(this->Makefile->GetDefinition("CMAKE_TRY_COMPILE_OSX_ARCHITECTURES")!=0)
259 std::string flag="-DCMAKE_OSX_ARCHITECTURES=";
260 flag += this->Makefile->GetSafeDefinition(
261 "CMAKE_TRY_COMPILE_OSX_ARCHITECTURES");
262 cmakeFlags.push_back(flag);
264 else if (this->Makefile->GetDefinition("CMAKE_OSX_ARCHITECTURES")!=0)
266 std::string flag="-DCMAKE_OSX_ARCHITECTURES=";
267 flag += this->Makefile->GetSafeDefinition("CMAKE_OSX_ARCHITECTURES");
268 cmakeFlags.push_back(flag);
270 /* on APPLE also pass CMAKE_OSX_SYSROOT to the try_compile */
271 if(this->Makefile->GetDefinition("CMAKE_OSX_SYSROOT")!=0)
273 std::string flag="-DCMAKE_OSX_SYSROOT=";
274 flag += this->Makefile->GetSafeDefinition("CMAKE_OSX_SYSROOT");
275 cmakeFlags.push_back(flag);
277 /* on APPLE also pass CMAKE_OSX_DEPLOYMENT_TARGET to the try_compile */
278 if(this->Makefile->GetDefinition("CMAKE_OSX_DEPLOYMENT_TARGET")!=0)
280 std::string flag="-DCMAKE_OSX_DEPLOYMENT_TARGET=";
281 flag += this->Makefile->GetSafeDefinition("CMAKE_OSX_DEPLOYMENT_TARGET");
282 cmakeFlags.push_back(flag);
285 /* Use a random file name to avoid rapid creation and deletion
286 of the same executable name (some filesystems fail on that). */
287 sprintf(targetNameBuf, "cmTryCompileExec%u",
288 cmSystemTools::RandomSeed());
289 targetName = targetNameBuf;
291 /* Put the executable at a known location (for COPY_FILE). */
292 fprintf(fout, "SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY \"%s\")\n",
293 this->BinaryDirectory.c_str());
294 /* Create the actual executable. */
295 fprintf(fout, "ADD_EXECUTABLE(%s \"%s\")\n", targetName, source.c_str());
296 fprintf(fout, "TARGET_LINK_LIBRARIES(%s ${LINK_LIBRARIES})\n",targetName);
298 projectName = "CMAKE_TRY_COMPILE";
299 // if the source is not in CMakeTmp
300 if(source.find("CMakeTmp") == source.npos)
302 this->Makefile->AddCMakeDependFile(source.c_str());
306 // else the srcdir bindir project target signature
309 projectName = argv[3].c_str();
311 if (argv.size() - extraArgs == 5)
313 targetName = argv[4].c_str();
317 bool erroroc = cmSystemTools::GetErrorOccuredFlag();
318 cmSystemTools::ResetErrorOccuredFlag();
320 // actually do the try compile now that everything is setup
321 int res = this->Makefile->TryCompile(sourceDirectory,
322 this->BinaryDirectory.c_str(),
325 this->SrcFileSignature,
330 cmSystemTools::SetErrorOccured();
333 // set the result var to the return value to indicate success or failure
334 this->Makefile->AddCacheDefinition(argv[0].c_str(),
335 (res == 0 ? "TRUE" : "FALSE"),
336 "Result of TRY_COMPILE",
337 cmCacheManager::INTERNAL);
339 if ( outputVariable.size() > 0 )
341 this->Makefile->AddDefinition(outputVariable.c_str(), output.c_str());
344 if (this->SrcFileSignature)
346 this->FindOutputFile(targetName);
348 if ((res==0) && (copyFile.size()))
350 if(this->OutputFile.empty() ||
351 !cmSystemTools::CopyFileAlways(this->OutputFile.c_str(),
354 cmOStringStream emsg;
355 emsg << "Cannot copy output executable\n"
356 << " '" << this->OutputFile.c_str() << "'\n"
357 << "to destination specified by COPY_FILE:\n"
358 << " '" << copyFile.c_str() << "'\n";
359 if(!this->FindErrorMessage.empty())
361 emsg << this->FindErrorMessage.c_str();
363 this->Makefile->IssueMessage(cmake::FATAL_ERROR, emsg.str());
371 void cmCoreTryCompile::CleanupFiles(const char* binDir)
378 std::string bdir = binDir;
379 if(bdir.find("CMakeTmp") == std::string::npos)
381 cmSystemTools::Error(
382 "TRY_COMPILE attempt to remove -rf directory that does not contain "
383 "CMakeTmp:", binDir);
387 cmsys::Directory dir;
390 std::set<cmStdString> deletedFiles;
391 for (fileNum = 0; fileNum < dir.GetNumberOfFiles(); ++fileNum)
393 if (strcmp(dir.GetFile(static_cast<unsigned long>(fileNum)),".") &&
394 strcmp(dir.GetFile(static_cast<unsigned long>(fileNum)),".."))
397 if(deletedFiles.find( dir.GetFile(static_cast<unsigned long>(fileNum)))
398 == deletedFiles.end())
400 deletedFiles.insert(dir.GetFile(static_cast<unsigned long>(fileNum)));
401 std::string fullPath = binDir;
403 fullPath += dir.GetFile(static_cast<unsigned long>(fileNum));
404 if(cmSystemTools::FileIsDirectory(fullPath.c_str()))
406 this->CleanupFiles(fullPath.c_str());
410 // Sometimes anti-virus software hangs on to new files so we
411 // cannot delete them immediately. Try a few times.
413 while(!cmSystemTools::RemoveFile(fullPath.c_str()) &&
414 --tries && cmSystemTools::FileExists(fullPath.c_str()))
416 cmSystemTools::Delay(500);
420 std::string m = "Remove failed on file: " + fullPath;
421 cmSystemTools::ReportLastSystemError(m.c_str());
429 void cmCoreTryCompile::FindOutputFile(const char* targetName)
431 this->FindErrorMessage = "";
432 this->OutputFile = "";
433 std::string tmpOutputFile = "/";
434 tmpOutputFile += targetName;
435 tmpOutputFile +=this->Makefile->GetSafeDefinition("CMAKE_EXECUTABLE_SUFFIX");
437 // a list of directories where to search for the compilation result
438 // at first directly in the binary dir
439 std::vector<std::string> searchDirs;
440 searchDirs.push_back("");
442 const char* config = this->Makefile->GetDefinition(
443 "CMAKE_TRY_COMPILE_CONFIGURATION");
444 // if a config was specified try that first
445 if (config && config[0])
447 std::string tmp = "/";
449 searchDirs.push_back(tmp);
451 searchDirs.push_back("/Debug");
452 searchDirs.push_back("/Development");
454 for(std::vector<std::string>::const_iterator it = searchDirs.begin();
455 it != searchDirs.end();
458 std::string command = this->BinaryDirectory;
460 command += tmpOutputFile;
461 if(cmSystemTools::FileExists(command.c_str()))
463 tmpOutputFile = cmSystemTools::CollapseFullPath(command.c_str());
464 this->OutputFile = tmpOutputFile;
469 cmOStringStream emsg;
470 emsg << "Unable to find the executable at any of:\n";
471 for (unsigned int i = 0; i < searchDirs.size(); ++i)
473 emsg << " " << this->BinaryDirectory << searchDirs[i]
474 << tmpOutputFile << "\n";
476 this->FindErrorMessage = emsg.str();