Resync
[CMakeLuaTailorHgBridge.git] / CMakeLua / Source / cmCoreTryCompile.cxx
blob5a74d69cb4fd59ccad866ff9429e32103fee79a6
1 /*=========================================================================
3 Program: CMake - Cross-Platform Makefile Generator
4 Module: $RCSfile: cmCoreTryCompile.cxx,v $
5 Language: C++
6 Date: $Date: 2009-01-29 20:23:18 $
7 Version: $Revision: 1.12 $
9 Copyright (c) 2007 Kitware, Inc., Insight Consortium. All rights reserved.
10 See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.
12 This software is distributed WITHOUT ANY WARRANTY; without even
13 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
14 PURPOSE. See the above copyright notices for more information.
16 =========================================================================*/
17 #include "cmCoreTryCompile.h"
18 #include "cmake.h"
19 #include "cmCacheManager.h"
20 #include "cmGlobalGenerator.h"
21 #include <cmsys/Directory.hxx>
23 int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv)
25 this->BinaryDirectory = argv[1].c_str();
26 this->OutputFile = "";
27 // which signature were we called with ?
28 this->SrcFileSignature = false;
29 unsigned int i;
31 const char* sourceDirectory = argv[2].c_str();
32 const char* projectName = 0;
33 const char* targetName = 0;
34 int extraArgs = 0;
36 // look for CMAKE_FLAGS and store them
37 std::vector<std::string> cmakeFlags;
38 for (i = 3; i < argv.size(); ++i)
40 if (argv[i] == "CMAKE_FLAGS")
42 // CMAKE_FLAGS is the first argument because we need an argv[0] that
43 // is not used, so it matches regular command line parsing which has
44 // the program name as arg 0
45 for (; i < argv.size() && argv[i] != "COMPILE_DEFINITIONS" &&
46 argv[i] != "OUTPUT_VARIABLE";
47 ++i)
49 extraArgs++;
50 cmakeFlags.push_back(argv[i]);
52 break;
56 // look for OUTPUT_VARIABLE and store them
57 std::string outputVariable;
58 for (i = 3; i < argv.size(); ++i)
60 if (argv[i] == "OUTPUT_VARIABLE")
62 if ( argv.size() <= (i+1) )
64 cmSystemTools::Error(
65 "OUTPUT_VARIABLE specified but there is no variable");
66 return -1;
68 extraArgs += 2;
69 outputVariable = argv[i+1];
70 break;
74 // look for COMPILE_DEFINITIONS and store them
75 std::vector<std::string> compileFlags;
76 for (i = 3; i < argv.size(); ++i)
78 if (argv[i] == "COMPILE_DEFINITIONS")
80 extraArgs++;
81 for (i = i + 1; i < argv.size() && argv[i] != "CMAKE_FLAGS" &&
82 argv[i] != "OUTPUT_VARIABLE";
83 ++i)
85 extraArgs++;
86 compileFlags.push_back(argv[i]);
88 break;
92 // look for COPY_FILE
93 std::string copyFile;
94 for (i = 3; i < argv.size(); ++i)
96 if (argv[i] == "COPY_FILE")
98 if ( argv.size() <= (i+1) )
100 cmSystemTools::Error(
101 "COPY_FILE specified but there is no variable");
102 return -1;
104 extraArgs += 2;
105 copyFile = argv[i+1];
106 break;
110 // do we have a srcfile signature
111 if (argv.size() - extraArgs == 3)
113 this->SrcFileSignature = true;
116 // compute the binary dir when TRY_COMPILE is called with a src file
117 // signature
118 if (this->SrcFileSignature)
120 this->BinaryDirectory += cmake::GetCMakeFilesDirectory();
121 this->BinaryDirectory += "/CMakeTmp";
123 else
125 // only valid for srcfile signatures
126 if (compileFlags.size())
128 cmSystemTools::Error(
129 "COMPILE_FLAGS specified on a srcdir type TRY_COMPILE");
130 return -1;
132 if (copyFile.size())
134 cmSystemTools::Error("COPY_FILE specified on a srcdir type TRY_COMPILE");
135 return -1;
138 // make sure the binary directory exists
139 cmSystemTools::MakeDirectory(this->BinaryDirectory.c_str());
141 // do not allow recursive try Compiles
142 if (this->BinaryDirectory == this->Makefile->GetHomeOutputDirectory())
144 cmSystemTools::Error(
145 "Attempt at a recursive or nested TRY_COMPILE in directory ",
146 this->BinaryDirectory.c_str());
147 return -1;
150 std::string outFileName = this->BinaryDirectory + "/CMakeLists.txt";
151 // which signature are we using? If we are using var srcfile bindir
152 if (this->SrcFileSignature)
154 // remove any CMakeCache.txt files so we will have a clean test
155 std::string ccFile = this->BinaryDirectory + "/CMakeCache.txt";
156 cmSystemTools::RemoveFile(ccFile.c_str());
158 // we need to create a directory and CMakeList file etc...
159 // first create the directories
160 sourceDirectory = this->BinaryDirectory.c_str();
162 // now create a CMakeList.txt file in that directory
163 FILE *fout = fopen(outFileName.c_str(),"w");
164 if (!fout)
166 cmSystemTools::Error("Failed to create CMakeList file for ",
167 outFileName.c_str());
168 cmSystemTools::ReportLastSystemError("");
169 return -1;
172 std::string source = argv[2];
173 std::string ext = cmSystemTools::GetFilenameExtension(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)\n",
178 cmVersion::GetMajorVersion(), cmVersion::GetMinorVersion());
179 if(def)
181 fprintf(fout, "SET(CMAKE_MODULE_PATH %s)\n", def);
183 if(lang)
185 fprintf(fout, "PROJECT(CMAKE_TRY_COMPILE %s)\n", lang);
187 else
189 cmOStringStream err;
190 err << "Unknown extension \"" << ext << "\" for file \""
191 << source << "\". TRY_COMPILE only works for enabled languages.\n"
192 << "Currently enabled languages are:";
193 std::vector<std::string> langs;
194 this->Makefile->GetCMakeInstance()->GetGlobalGenerator()->
195 GetEnabledLanguages(langs);
196 for(std::vector<std::string>::iterator l = langs.begin();
197 l != langs.end(); ++l)
199 err << " " << *l;
201 err << "\nSee PROJECT command for help enabling other languages.";
202 cmSystemTools::Error(err.str().c_str());
203 fclose(fout);
204 return -1;
206 std::string langFlags = "CMAKE_";
207 langFlags += lang;
208 langFlags += "_FLAGS";
209 fprintf(fout, "SET(CMAKE_VERBOSE_MAKEFILE 1)\n");
210 fprintf(fout, "SET(CMAKE_%s_FLAGS \"", lang);
211 const char* flags = this->Makefile->GetDefinition(langFlags.c_str());
212 if(flags)
214 fprintf(fout, " %s ", flags);
216 fprintf(fout, " ${COMPILE_DEFINITIONS}\")\n");
217 fprintf(fout, "INCLUDE_DIRECTORIES(${INCLUDE_DIRECTORIES})\n");
218 fprintf(fout, "SET(CMAKE_SUPPRESS_REGENERATION 1)\n");
219 fprintf(fout, "LINK_DIRECTORIES(${LINK_DIRECTORIES})\n");
220 // handle any compile flags we need to pass on
221 if (compileFlags.size())
223 fprintf(fout, "ADD_DEFINITIONS( ");
224 for (i = 0; i < compileFlags.size(); ++i)
226 fprintf(fout,"%s ",compileFlags[i].c_str());
228 fprintf(fout, ")\n");
231 /* for the TRY_COMPILEs we want to be able to specify the architecture.
232 So the user can set CMAKE_OSX_ARCHITECTURE to i386;ppc and then set
233 CMAKE_TRY_COMPILE_OSX_ARCHITECTURE first to i386 and then to ppc to
234 have the tests run for each specific architecture. Since
235 cmLocalGenerator doesn't allow building for "the other"
236 architecture only via CMAKE_OSX_ARCHITECTURES,use to CMAKE_DO_TRY_COMPILE
237 to enforce it for this case here.
239 cmakeFlags.push_back("-DCMAKE_DO_TRY_COMPILE=TRUE");
240 if(this->Makefile->GetDefinition("CMAKE_TRY_COMPILE_OSX_ARCHITECTURES")!=0)
242 std::string flag="-DCMAKE_OSX_ARCHITECTURES=";
243 flag += this->Makefile->GetSafeDefinition(
244 "CMAKE_TRY_COMPILE_OSX_ARCHITECTURES");
245 cmakeFlags.push_back(flag);
247 else if (this->Makefile->GetDefinition("CMAKE_OSX_ARCHITECTURES")!=0)
249 std::string flag="-DCMAKE_OSX_ARCHITECTURES=";
250 flag += this->Makefile->GetSafeDefinition("CMAKE_OSX_ARCHITECTURES");
251 cmakeFlags.push_back(flag);
254 fprintf(fout, "ADD_EXECUTABLE(cmTryCompileExec \"%s\")\n",source.c_str());
255 fprintf(fout,
256 "TARGET_LINK_LIBRARIES(cmTryCompileExec ${LINK_LIBRARIES})\n");
257 fclose(fout);
258 projectName = "CMAKE_TRY_COMPILE";
259 targetName = "cmTryCompileExec";
260 // if the source is not in CMakeTmp
261 if(source.find("CMakeTmp") == source.npos)
263 this->Makefile->AddCMakeDependFile(source.c_str());
267 // else the srcdir bindir project target signature
268 else
270 projectName = argv[3].c_str();
272 if (argv.size() - extraArgs == 5)
274 targetName = argv[4].c_str();
278 bool erroroc = cmSystemTools::GetErrorOccuredFlag();
279 cmSystemTools::ResetErrorOccuredFlag();
280 std::string output;
281 // actually do the try compile now that everything is setup
282 int res = this->Makefile->TryCompile(sourceDirectory,
283 this->BinaryDirectory.c_str(),
284 projectName,
285 targetName,
286 &cmakeFlags,
287 &output);
288 if ( erroroc )
290 cmSystemTools::SetErrorOccured();
293 // set the result var to the return value to indicate success or failure
294 this->Makefile->AddCacheDefinition(argv[0].c_str(),
295 (res == 0 ? "TRUE" : "FALSE"),
296 "Result of TRY_COMPILE",
297 cmCacheManager::INTERNAL);
299 if ( outputVariable.size() > 0 )
301 this->Makefile->AddDefinition(outputVariable.c_str(), output.c_str());
304 if (this->SrcFileSignature)
306 this->FindOutputFile(targetName);
308 if ((res==0) && (copyFile.size()))
310 if(!cmSystemTools::CopyFileAlways(this->OutputFile.c_str(),
311 copyFile.c_str()))
313 cmOStringStream emsg;
314 emsg << "Could not COPY_FILE.\n"
315 << " OutputFile: '" << this->OutputFile.c_str() << "'\n"
316 << " copyFile: '" << copyFile.c_str() << "'\n";
318 if (this->FindErrorMessage.size())
320 emsg << "\n";
321 emsg << this->FindErrorMessage.c_str() << "\n";
324 cmSystemTools::Error(emsg.str().c_str());
325 return -1;
329 return res;
332 void cmCoreTryCompile::CleanupFiles(const char* binDir)
334 if ( !binDir )
336 return;
339 std::string bdir = binDir;
340 if(bdir.find("CMakeTmp") == std::string::npos)
342 cmSystemTools::Error(
343 "TRY_COMPILE attempt to remove -rf directory that does not contain "
344 "CMakeTmp:", binDir);
345 return;
348 cmsys::Directory dir;
349 dir.Load(binDir);
350 size_t fileNum;
351 std::set<cmStdString> deletedFiles;
352 for (fileNum = 0; fileNum < dir.GetNumberOfFiles(); ++fileNum)
354 if (strcmp(dir.GetFile(static_cast<unsigned long>(fileNum)),".") &&
355 strcmp(dir.GetFile(static_cast<unsigned long>(fileNum)),".."))
358 if(deletedFiles.find( dir.GetFile(static_cast<unsigned long>(fileNum)))
359 == deletedFiles.end())
361 deletedFiles.insert(dir.GetFile(static_cast<unsigned long>(fileNum)));
362 std::string fullPath = binDir;
363 fullPath += "/";
364 fullPath += dir.GetFile(static_cast<unsigned long>(fileNum));
365 if(cmSystemTools::FileIsDirectory(fullPath.c_str()))
367 this->CleanupFiles(fullPath.c_str());
369 else
371 if(!cmSystemTools::RemoveFile(fullPath.c_str()))
373 bool removed = false;
374 int numAttempts = 0;
375 // sometimes anti-virus software hangs on to
376 // new files and we can not delete them, so try
377 // 5 times with .5 second delay between tries.
378 while(!removed && numAttempts < 5)
380 cmSystemTools::Delay(500);
381 if(cmSystemTools::RemoveFile(fullPath.c_str()))
383 removed = true;
385 numAttempts++;
387 if(!removed)
389 std::string m = "Remove failed on file: ";
390 m += fullPath;
391 cmSystemTools::ReportLastSystemError(m.c_str());
400 void cmCoreTryCompile::FindOutputFile(const char* targetName)
402 this->FindErrorMessage = "";
403 this->OutputFile = "";
404 std::string tmpOutputFile = "/";
405 tmpOutputFile += targetName;
406 tmpOutputFile +=this->Makefile->GetSafeDefinition("CMAKE_EXECUTABLE_SUFFIX");
408 // a list of directories where to search for the compilation result
409 // at first directly in the binary dir
410 std::vector<std::string> searchDirs;
411 searchDirs.push_back("");
413 const char* config = this->Makefile->GetDefinition(
414 "CMAKE_TRY_COMPILE_CONFIGURATION");
415 // if a config was specified try that first
416 if (config && config[0])
418 std::string tmp = "/";
419 tmp += config;
420 searchDirs.push_back(tmp);
422 searchDirs.push_back("/Debug");
423 searchDirs.push_back("/Development");
425 for(std::vector<std::string>::const_iterator it = searchDirs.begin();
426 it != searchDirs.end();
427 ++it)
429 std::string command = this->BinaryDirectory;
430 command += *it;
431 command += tmpOutputFile;
432 if(cmSystemTools::FileExists(command.c_str()))
434 tmpOutputFile = cmSystemTools::CollapseFullPath(command.c_str());
435 this->OutputFile = tmpOutputFile;
436 return;
440 cmOStringStream emsg;
441 emsg << "Unable to find executable for " << this->GetName() << ": tried \"";
442 for (unsigned int i = 0; i < searchDirs.size(); ++i)
444 emsg << this->BinaryDirectory << searchDirs[i] << tmpOutputFile;
445 if (i < searchDirs.size() - 1)
447 emsg << "\" and \"";
449 else
451 emsg << "\".";
454 this->FindErrorMessage = emsg.str();
455 return;