Initial commit.
[CMakeLuaTailorHgBridge.git] / CMakeLua / Source / cmCoreTryCompile.cxx
blob6a5eb8550a5f9ad713764c71ac42b9447eee9925
1 /*=========================================================================
3 Program: CMake - Cross-Platform Makefile Generator
4 Module: $RCSfile: cmCoreTryCompile.cxx,v $
5 Language: C++
6 Date: $Date: 2007/12/06 14:56:02 $
7 Version: $Revision: 1.5 $
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)
26 this->BinaryDirectory = argv[1].c_str();
27 this->OutputFile = "";
28 // which signature were we called with ?
29 this->SrcFileSignature = false;
30 unsigned int i;
32 const char* sourceDirectory = argv[2].c_str();
33 const char* projectName = 0;
34 const char* targetName = 0;
35 int extraArgs = 0;
37 // look for CMAKE_FLAGS and store them
38 std::vector<std::string> cmakeFlags;
39 for (i = 3; i < argv.size(); ++i)
41 if (argv[i] == "CMAKE_FLAGS")
43 // CMAKE_FLAGS is the first argument because we need an argv[0] that
44 // is not used, so it matches regular command line parsing which has
45 // the program name as arg 0
46 for (; i < argv.size() && argv[i] != "COMPILE_DEFINITIONS" &&
47 argv[i] != "OUTPUT_VARIABLE";
48 ++i)
50 extraArgs++;
51 cmakeFlags.push_back(argv[i]);
53 break;
57 // look for OUTPUT_VARIABLE and store them
58 std::string outputVariable;
59 for (i = 3; i < argv.size(); ++i)
61 if (argv[i] == "OUTPUT_VARIABLE")
63 if ( argv.size() <= (i+1) )
65 cmSystemTools::Error(
66 "OUTPUT_VARIABLE specified but there is no variable");
67 return -1;
69 extraArgs += 2;
70 outputVariable = argv[i+1];
71 break;
75 // look for COMPILE_DEFINITIONS and store them
76 std::vector<std::string> compileFlags;
77 for (i = 3; i < argv.size(); ++i)
79 if (argv[i] == "COMPILE_DEFINITIONS")
81 extraArgs++;
82 for (i = i + 1; i < argv.size() && argv[i] != "CMAKE_FLAGS" &&
83 argv[i] != "OUTPUT_VARIABLE";
84 ++i)
86 extraArgs++;
87 compileFlags.push_back(argv[i]);
89 break;
93 // look for COPY_FILE
94 std::string copyFile;
95 for (i = 3; i < argv.size(); ++i)
97 if (argv[i] == "COPY_FILE")
99 if ( argv.size() <= (i+1) )
101 cmSystemTools::Error(
102 "COPY_FILE specified but there is no variable");
103 return -1;
105 extraArgs += 2;
106 copyFile = argv[i+1];
107 break;
111 // do we have a srcfile signature
112 if (argv.size() - extraArgs == 3)
114 this->SrcFileSignature = true;
117 // compute the binary dir when TRY_COMPILE is called with a src file
118 // signature
119 if (this->SrcFileSignature)
121 this->BinaryDirectory += cmake::GetCMakeFilesDirectory();
122 this->BinaryDirectory += "/CMakeTmp";
124 else
126 // only valid for srcfile signatures
127 if (compileFlags.size())
129 cmSystemTools::Error(
130 "COMPILE_FLAGS specified on a srcdir type TRY_COMPILE");
131 return -1;
133 if (copyFile.size())
135 cmSystemTools::Error("COPY_FILE specified on a srcdir type TRY_COMPILE");
136 return -1;
139 // make sure the binary directory exists
140 cmSystemTools::MakeDirectory(this->BinaryDirectory.c_str());
142 // do not allow recursive try Compiles
143 if (this->BinaryDirectory == this->Makefile->GetHomeOutputDirectory())
145 cmSystemTools::Error(
146 "Attempt at a recursive or nested TRY_COMPILE in directory ",
147 this->BinaryDirectory.c_str());
148 return -1;
151 std::string outFileName = this->BinaryDirectory + "/CMakeLists.txt";
152 // which signature are we using? If we are using var srcfile bindir
153 if (this->SrcFileSignature)
155 // remove any CMakeCache.txt files so we will have a clean test
156 std::string ccFile = this->BinaryDirectory + "/CMakeCache.txt";
157 cmSystemTools::RemoveFile(ccFile.c_str());
159 // we need to create a directory and CMakeList file etc...
160 // first create the directories
161 sourceDirectory = this->BinaryDirectory.c_str();
163 // now create a CMakeList.txt file in that directory
164 FILE *fout = fopen(outFileName.c_str(),"w");
165 if (!fout)
167 cmSystemTools::Error("Failed to create CMakeList file for ",
168 outFileName.c_str());
169 cmSystemTools::ReportLastSystemError("");
170 return -1;
173 std::string source = argv[2];
174 std::string ext = cmSystemTools::GetFilenameExtension(source);
175 const char* lang =(this->Makefile->GetCMakeInstance()->GetGlobalGenerator()
176 ->GetLanguageFromExtension(ext.c_str()));
177 const char* def = this->Makefile->GetDefinition("CMAKE_MODULE_PATH");
178 if(def)
180 fprintf(fout, "SET(CMAKE_MODULE_PATH %s)\n", def);
182 if(lang)
184 fprintf(fout, "PROJECT(CMAKE_TRY_COMPILE %s)\n", lang);
186 else
188 cmOStringStream err;
189 err << "Unknown extension \"" << ext << "\" for file \""
190 << source << "\". TRY_COMPILE only works for enabled languages.\n"
191 << "Currently enabled languages are:";
192 std::vector<std::string> langs;
193 this->Makefile->GetCMakeInstance()->GetGlobalGenerator()->
194 GetEnabledLanguages(langs);
195 for(std::vector<std::string>::iterator l = langs.begin();
196 l != langs.end(); ++l)
198 err << " " << *l;
200 err << "\nSee PROJECT command for help enabling other languages.";
201 cmSystemTools::Error(err.str().c_str());
202 fclose(fout);
203 return -1;
205 std::string langFlags = "CMAKE_";
206 langFlags += lang;
207 langFlags += "_FLAGS";
208 fprintf(fout, "SET(CMAKE_VERBOSE_MAKEFILE 1)\n");
209 fprintf(fout, "SET(CMAKE_%s_FLAGS \"", lang);
210 const char* flags = this->Makefile->GetDefinition(langFlags.c_str());
211 if(flags)
213 fprintf(fout, " %s ", flags);
215 fprintf(fout, " ${COMPILE_DEFINITIONS}\")\n");
216 fprintf(fout, "INCLUDE_DIRECTORIES(${INCLUDE_DIRECTORIES})\n");
217 fprintf(fout, "SET(CMAKE_SUPPRESS_REGENERATION 1)\n");
218 fprintf(fout, "LINK_DIRECTORIES(${LINK_DIRECTORIES})\n");
219 // handle any compile flags we need to pass on
220 if (compileFlags.size())
222 fprintf(fout, "ADD_DEFINITIONS( ");
223 for (i = 0; i < compileFlags.size(); ++i)
225 fprintf(fout,"%s ",compileFlags[i].c_str());
227 fprintf(fout, ")\n");
230 /* for the TRY_COMPILEs we want to be able to specify the architecture.
231 So the user can set CMAKE_OSX_ARCHITECTURE to i386;ppc and then set
232 CMAKE_TRY_COMPILE_OSX_ARCHITECTURE first to i386 and then to ppc to
233 have the tests run for each specific architecture. Since
234 cmLocalGenerator doesn't allow building for "the other"
235 architecture only via CMAKE_OSX_ARCHITECTURES,use to CMAKE_DO_TRY_COMPILE
236 to enforce it for this case here.
238 cmakeFlags.push_back("-DCMAKE_DO_TRY_COMPILE=TRUE");
239 if(this->Makefile->GetDefinition("CMAKE_TRY_COMPILE_OSX_ARCHITECTURES")!=0)
241 std::string flag="-DCMAKE_OSX_ARCHITECTURES=";
242 flag += this->Makefile->GetSafeDefinition(
243 "CMAKE_TRY_COMPILE_OSX_ARCHITECTURES");
244 cmakeFlags.push_back(flag);
246 else if (this->Makefile->GetDefinition("CMAKE_OSX_ARCHITECTURES")!=0)
248 std::string flag="-DCMAKE_OSX_ARCHITECTURES=";
249 flag += this->Makefile->GetSafeDefinition("CMAKE_OSX_ARCHITECTURES");
250 cmakeFlags.push_back(flag);
253 fprintf(fout, "ADD_EXECUTABLE(cmTryCompileExec \"%s\")\n",source.c_str());
254 fprintf(fout,
255 "TARGET_LINK_LIBRARIES(cmTryCompileExec ${LINK_LIBRARIES})\n");
256 fclose(fout);
257 projectName = "CMAKE_TRY_COMPILE";
258 targetName = "cmTryCompileExec";
259 // if the source is not in CMakeTmp
260 if(source.find("CMakeTmp") == source.npos)
262 this->Makefile->AddCMakeDependFile(source.c_str());
266 // else the srcdir bindir project target signature
267 else
269 projectName = argv[3].c_str();
271 if (argv.size() - extraArgs == 5)
273 targetName = argv[4].c_str();
277 bool erroroc = cmSystemTools::GetErrorOccuredFlag();
278 cmSystemTools::ResetErrorOccuredFlag();
279 std::string output;
280 // actually do the try compile now that everything is setup
281 int res = this->Makefile->TryCompile(sourceDirectory,
282 this->BinaryDirectory.c_str(),
283 projectName,
284 targetName,
285 &cmakeFlags,
286 &output);
287 if ( erroroc )
289 cmSystemTools::SetErrorOccured();
292 // set the result var to the return value to indicate success or failure
293 this->Makefile->AddCacheDefinition(argv[0].c_str(),
294 (res == 0 ? "TRUE" : "FALSE"),
295 "Result of TRY_COMPILE",
296 cmCacheManager::INTERNAL);
298 if ( outputVariable.size() > 0 )
300 this->Makefile->AddDefinition(outputVariable.c_str(), output.c_str());
303 if (this->SrcFileSignature)
305 this->FindOutputFile(targetName);
306 if ((res==0) && (copyFile.size()))
308 if(!cmSystemTools::CopyFileAlways(this->OutputFile.c_str(),
309 copyFile.c_str()))
311 cmSystemTools::Error("Could not COPY_FILE");
312 return -1;
316 return res;
319 void cmCoreTryCompile::CleanupFiles(const char* binDir)
321 if ( !binDir )
323 return;
326 std::string bdir = binDir;
327 if(bdir.find("CMakeTmp") == std::string::npos)
329 cmSystemTools::Error(
330 "TRY_COMPILE attempt to remove -rf directory that does not contain "
331 "CMakeTmp:", binDir);
332 return;
335 cmsys::Directory dir;
336 dir.Load(binDir);
337 size_t fileNum;
338 std::set<cmStdString> deletedFiles;
339 for (fileNum = 0; fileNum < dir.GetNumberOfFiles(); ++fileNum)
341 if (strcmp(dir.GetFile(static_cast<unsigned long>(fileNum)),".") &&
342 strcmp(dir.GetFile(static_cast<unsigned long>(fileNum)),".."))
345 if(deletedFiles.find( dir.GetFile(static_cast<unsigned long>(fileNum)))
346 == deletedFiles.end())
348 deletedFiles.insert(dir.GetFile(static_cast<unsigned long>(fileNum)));
349 std::string fullPath = binDir;
350 fullPath += "/";
351 fullPath += dir.GetFile(static_cast<unsigned long>(fileNum));
352 if(cmSystemTools::FileIsDirectory(fullPath.c_str()))
354 this->CleanupFiles(fullPath.c_str());
356 else
358 if(!cmSystemTools::RemoveFile(fullPath.c_str()))
360 std::string m = "Remove failed on file: ";
361 m += fullPath;
362 cmSystemTools::ReportLastSystemError(m.c_str());
370 void cmCoreTryCompile::FindOutputFile(const char* targetName)
372 this->FindErrorMessage = "";
373 this->OutputFile = "";
374 std::string tmpOutputFile = "/";
375 tmpOutputFile += targetName;
376 tmpOutputFile +=this->Makefile->GetSafeDefinition("CMAKE_EXECUTABLE_SUFFIX");
378 // a list of directories where to search for the compilation result
379 // at first directly in the binary dir
380 std::vector<std::string> searchDirs;
381 searchDirs.push_back("");
383 const char* config = this->Makefile->GetDefinition(
384 "CMAKE_TRY_COMPILE_CONFIGURATION");
385 // if a config was specified try that first
386 if (config && config[0])
388 std::string tmp = "/";
389 tmp += config;
390 searchDirs.push_back(tmp);
392 searchDirs.push_back("/Debug");
393 searchDirs.push_back("/Development");
395 for(std::vector<std::string>::const_iterator it = searchDirs.begin();
396 it != searchDirs.end();
397 ++it)
399 std::string command = this->BinaryDirectory;
400 command += *it;
401 command += tmpOutputFile;
402 if(cmSystemTools::FileExists(command.c_str()))
404 tmpOutputFile = cmSystemTools::CollapseFullPath(command.c_str());
405 this->OutputFile = tmpOutputFile;
406 return;
410 cmOStringStream emsg;
411 emsg << "Unable to find executable for " << this->GetName() << ": tried \"";
412 for (unsigned int i = 0; i < searchDirs.size(); ++i)
414 emsg << this->BinaryDirectory << searchDirs[i] << tmpOutputFile;
415 if (i < searchDirs.size() - 1)
417 emsg << "\" and \"";
419 else
421 emsg << "\".";
424 this->FindErrorMessage = emsg.str();
425 return;