Resync
[CMakeLuaTailorHgBridge.git] / CMakeLua / Source / cmCoreTryCompile.cxx
blobee5b34ceb8003960b66a285182d87963d111d4d7
1 /*=========================================================================
3 Program: CMake - Cross-Platform Makefile Generator
4 Module: $RCSfile: cmCoreTryCompile.cxx,v $
5 Language: C++
6 Date: $Date: 2008-03-07 13:53:22 $
7 Version: $Revision: 1.7 $
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 fprintf(fout, "cmake_policy(VERSION %u.%u)\n",
179 cmVersion::GetMajorVersion(), cmVersion::GetMinorVersion());
180 if(def)
182 fprintf(fout, "SET(CMAKE_MODULE_PATH %s)\n", def);
184 if(lang)
186 fprintf(fout, "PROJECT(CMAKE_TRY_COMPILE %s)\n", lang);
188 else
190 cmOStringStream err;
191 err << "Unknown extension \"" << ext << "\" for file \""
192 << source << "\". TRY_COMPILE only works for enabled languages.\n"
193 << "Currently enabled languages are:";
194 std::vector<std::string> langs;
195 this->Makefile->GetCMakeInstance()->GetGlobalGenerator()->
196 GetEnabledLanguages(langs);
197 for(std::vector<std::string>::iterator l = langs.begin();
198 l != langs.end(); ++l)
200 err << " " << *l;
202 err << "\nSee PROJECT command for help enabling other languages.";
203 cmSystemTools::Error(err.str().c_str());
204 fclose(fout);
205 return -1;
207 std::string langFlags = "CMAKE_";
208 langFlags += lang;
209 langFlags += "_FLAGS";
210 fprintf(fout, "SET(CMAKE_VERBOSE_MAKEFILE 1)\n");
211 fprintf(fout, "SET(CMAKE_%s_FLAGS \"", lang);
212 const char* flags = this->Makefile->GetDefinition(langFlags.c_str());
213 if(flags)
215 fprintf(fout, " %s ", flags);
217 fprintf(fout, " ${COMPILE_DEFINITIONS}\")\n");
218 fprintf(fout, "INCLUDE_DIRECTORIES(${INCLUDE_DIRECTORIES})\n");
219 fprintf(fout, "SET(CMAKE_SUPPRESS_REGENERATION 1)\n");
220 fprintf(fout, "LINK_DIRECTORIES(${LINK_DIRECTORIES})\n");
221 // handle any compile flags we need to pass on
222 if (compileFlags.size())
224 fprintf(fout, "ADD_DEFINITIONS( ");
225 for (i = 0; i < compileFlags.size(); ++i)
227 fprintf(fout,"%s ",compileFlags[i].c_str());
229 fprintf(fout, ")\n");
232 /* for the TRY_COMPILEs we want to be able to specify the architecture.
233 So the user can set CMAKE_OSX_ARCHITECTURE to i386;ppc and then set
234 CMAKE_TRY_COMPILE_OSX_ARCHITECTURE first to i386 and then to ppc to
235 have the tests run for each specific architecture. Since
236 cmLocalGenerator doesn't allow building for "the other"
237 architecture only via CMAKE_OSX_ARCHITECTURES,use to CMAKE_DO_TRY_COMPILE
238 to enforce it for this case here.
240 cmakeFlags.push_back("-DCMAKE_DO_TRY_COMPILE=TRUE");
241 if(this->Makefile->GetDefinition("CMAKE_TRY_COMPILE_OSX_ARCHITECTURES")!=0)
243 std::string flag="-DCMAKE_OSX_ARCHITECTURES=";
244 flag += this->Makefile->GetSafeDefinition(
245 "CMAKE_TRY_COMPILE_OSX_ARCHITECTURES");
246 cmakeFlags.push_back(flag);
248 else if (this->Makefile->GetDefinition("CMAKE_OSX_ARCHITECTURES")!=0)
250 std::string flag="-DCMAKE_OSX_ARCHITECTURES=";
251 flag += this->Makefile->GetSafeDefinition("CMAKE_OSX_ARCHITECTURES");
252 cmakeFlags.push_back(flag);
255 fprintf(fout, "ADD_EXECUTABLE(cmTryCompileExec \"%s\")\n",source.c_str());
256 fprintf(fout,
257 "TARGET_LINK_LIBRARIES(cmTryCompileExec ${LINK_LIBRARIES})\n");
258 fclose(fout);
259 projectName = "CMAKE_TRY_COMPILE";
260 targetName = "cmTryCompileExec";
261 // if the source is not in CMakeTmp
262 if(source.find("CMakeTmp") == source.npos)
264 this->Makefile->AddCMakeDependFile(source.c_str());
268 // else the srcdir bindir project target signature
269 else
271 projectName = argv[3].c_str();
273 if (argv.size() - extraArgs == 5)
275 targetName = argv[4].c_str();
279 bool erroroc = cmSystemTools::GetErrorOccuredFlag();
280 cmSystemTools::ResetErrorOccuredFlag();
281 std::string output;
282 // actually do the try compile now that everything is setup
283 int res = this->Makefile->TryCompile(sourceDirectory,
284 this->BinaryDirectory.c_str(),
285 projectName,
286 targetName,
287 &cmakeFlags,
288 &output);
289 if ( erroroc )
291 cmSystemTools::SetErrorOccured();
294 // set the result var to the return value to indicate success or failure
295 this->Makefile->AddCacheDefinition(argv[0].c_str(),
296 (res == 0 ? "TRUE" : "FALSE"),
297 "Result of TRY_COMPILE",
298 cmCacheManager::INTERNAL);
300 if ( outputVariable.size() > 0 )
302 this->Makefile->AddDefinition(outputVariable.c_str(), output.c_str());
305 if (this->SrcFileSignature)
307 this->FindOutputFile(targetName);
308 if ((res==0) && (copyFile.size()))
310 if(!cmSystemTools::CopyFileAlways(this->OutputFile.c_str(),
311 copyFile.c_str()))
313 cmSystemTools::Error("Could not COPY_FILE");
314 return -1;
318 return res;
321 void cmCoreTryCompile::CleanupFiles(const char* binDir)
323 if ( !binDir )
325 return;
328 std::string bdir = binDir;
329 if(bdir.find("CMakeTmp") == std::string::npos)
331 cmSystemTools::Error(
332 "TRY_COMPILE attempt to remove -rf directory that does not contain "
333 "CMakeTmp:", binDir);
334 return;
337 cmsys::Directory dir;
338 dir.Load(binDir);
339 size_t fileNum;
340 std::set<cmStdString> deletedFiles;
341 for (fileNum = 0; fileNum < dir.GetNumberOfFiles(); ++fileNum)
343 if (strcmp(dir.GetFile(static_cast<unsigned long>(fileNum)),".") &&
344 strcmp(dir.GetFile(static_cast<unsigned long>(fileNum)),".."))
347 if(deletedFiles.find( dir.GetFile(static_cast<unsigned long>(fileNum)))
348 == deletedFiles.end())
350 deletedFiles.insert(dir.GetFile(static_cast<unsigned long>(fileNum)));
351 std::string fullPath = binDir;
352 fullPath += "/";
353 fullPath += dir.GetFile(static_cast<unsigned long>(fileNum));
354 if(cmSystemTools::FileIsDirectory(fullPath.c_str()))
356 this->CleanupFiles(fullPath.c_str());
358 else
360 if(!cmSystemTools::RemoveFile(fullPath.c_str()))
362 std::string m = "Remove failed on file: ";
363 m += fullPath;
364 cmSystemTools::ReportLastSystemError(m.c_str());
372 void cmCoreTryCompile::FindOutputFile(const char* targetName)
374 this->FindErrorMessage = "";
375 this->OutputFile = "";
376 std::string tmpOutputFile = "/";
377 tmpOutputFile += targetName;
378 tmpOutputFile +=this->Makefile->GetSafeDefinition("CMAKE_EXECUTABLE_SUFFIX");
380 // a list of directories where to search for the compilation result
381 // at first directly in the binary dir
382 std::vector<std::string> searchDirs;
383 searchDirs.push_back("");
385 const char* config = this->Makefile->GetDefinition(
386 "CMAKE_TRY_COMPILE_CONFIGURATION");
387 // if a config was specified try that first
388 if (config && config[0])
390 std::string tmp = "/";
391 tmp += config;
392 searchDirs.push_back(tmp);
394 searchDirs.push_back("/Debug");
395 searchDirs.push_back("/Development");
397 for(std::vector<std::string>::const_iterator it = searchDirs.begin();
398 it != searchDirs.end();
399 ++it)
401 std::string command = this->BinaryDirectory;
402 command += *it;
403 command += tmpOutputFile;
404 if(cmSystemTools::FileExists(command.c_str()))
406 tmpOutputFile = cmSystemTools::CollapseFullPath(command.c_str());
407 this->OutputFile = tmpOutputFile;
408 return;
412 cmOStringStream emsg;
413 emsg << "Unable to find executable for " << this->GetName() << ": tried \"";
414 for (unsigned int i = 0; i < searchDirs.size(); ++i)
416 emsg << this->BinaryDirectory << searchDirs[i] << tmpOutputFile;
417 if (i < searchDirs.size() - 1)
419 emsg << "\" and \"";
421 else
423 emsg << "\".";
426 this->FindErrorMessage = emsg.str();
427 return;