TZIVI-254: IVI needs a newer version of cmake
[profile/ivi/cmake.git] / Source / cmCoreTryCompile.cxx
1 /*============================================================================
2   CMake - Cross Platform Makefile Generator
3   Copyright 2000-2011 Kitware, Inc., Insight Software Consortium
4
5   Distributed under the OSI-approved BSD License (the "License");
6   see accompanying file Copyright.txt for details.
7
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"
13 #include "cmake.h"
14 #include "cmCacheManager.h"
15 #include "cmGlobalGenerator.h"
16 #include <cmsys/Directory.hxx>
17
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
26   const char* sourceDirectory = argv[2].c_str();
27   const char* projectName = 0;
28   const char* targetName = 0;
29   char targetNameBuf[64];
30   int extraArgs = 0;
31
32   // look for CMAKE_FLAGS and store them
33   std::vector<std::string> cmakeFlags;
34   for (i = 3; i < argv.size(); ++i)
35     {
36     if (argv[i] == "CMAKE_FLAGS")
37       {
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";
43            ++i)
44         {
45         extraArgs++;
46         cmakeFlags.push_back(argv[i]);
47         }
48       break;
49       }
50     }
51
52   // look for OUTPUT_VARIABLE and store them
53   std::string outputVariable;
54   for (i = 3; i < argv.size(); ++i)
55     {
56     if (argv[i] == "OUTPUT_VARIABLE")
57       {
58       if ( argv.size() <= (i+1) )
59         {
60         this->Makefile->IssueMessage(cmake::FATAL_ERROR,
61           "OUTPUT_VARIABLE specified but there is no variable");
62         return -1;
63         }
64       extraArgs += 2;
65       outputVariable = argv[i+1];
66       break;
67       }
68     }
69
70   // look for COMPILE_DEFINITIONS and store them
71   std::vector<std::string> compileFlags;
72   for (i = 3; i < argv.size(); ++i)
73     {
74     if (argv[i] == "COMPILE_DEFINITIONS")
75       {
76       extraArgs++;
77       for (i = i + 1; i < argv.size() && argv[i] != "CMAKE_FLAGS" &&
78              argv[i] != "OUTPUT_VARIABLE";
79            ++i)
80         {
81         extraArgs++;
82         compileFlags.push_back(argv[i]);
83         }
84       break;
85       }
86     }
87
88   // look for COPY_FILE
89   std::string copyFile;
90   for (i = 3; i < argv.size(); ++i)
91     {
92     if (argv[i] == "COPY_FILE")
93       {
94       if ( argv.size() <= (i+1) )
95         {
96         this->Makefile->IssueMessage(cmake::FATAL_ERROR,
97           "COPY_FILE specified but there is no variable");
98         return -1;
99         }
100       extraArgs += 2;
101       copyFile = argv[i+1];
102       break;
103       }
104     }
105
106   // do we have a srcfile signature
107   if (argv.size() - extraArgs == 3)
108     {
109     this->SrcFileSignature = true;
110     }
111
112   // compute the binary dir when TRY_COMPILE is called with a src file
113   // signature
114   if (this->SrcFileSignature)
115     {
116     this->BinaryDirectory += cmake::GetCMakeFilesDirectory();
117     this->BinaryDirectory += "/CMakeTmp";
118     }
119   else
120     {
121     // only valid for srcfile signatures
122     if (compileFlags.size())
123       {
124       this->Makefile->IssueMessage(cmake::FATAL_ERROR,
125         "COMPILE_FLAGS specified on a srcdir type TRY_COMPILE");
126       return -1;
127       }
128     if (copyFile.size())
129       {
130       this->Makefile->IssueMessage(cmake::FATAL_ERROR,
131         "COPY_FILE specified on a srcdir type TRY_COMPILE");
132       return -1;
133       }
134     }
135   // make sure the binary directory exists
136   cmSystemTools::MakeDirectory(this->BinaryDirectory.c_str());
137
138   // do not allow recursive try Compiles
139   if (this->BinaryDirectory == this->Makefile->GetHomeOutputDirectory())
140     {
141     cmOStringStream e;
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());
145     return -1;
146     }
147
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)
151     {
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());
155
156     // we need to create a directory and CMakeLists file etc...
157     // first create the directories
158     sourceDirectory = this->BinaryDirectory.c_str();
159
160     // now create a CMakeLists.txt file in that directory
161     FILE *fout = fopen(outFileName.c_str(),"w");
162     if (!fout)
163       {
164       cmOStringStream e;
165       e << "Failed to open\n"
166         << "  " << outFileName.c_str() << "\n"
167         << cmSystemTools::GetLastSystemError();
168       this->Makefile->IssueMessage(cmake::FATAL_ERROR, e.str());
169       return -1;
170       }
171
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());
180     if(def)
181       {
182       fprintf(fout, "SET(CMAKE_MODULE_PATH %s)\n", def);
183       }
184
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()))
190       {
191       fprintf(fout, "SET(%s \"%s\")\n",
192               rulesOverrideLang.c_str(), rulesOverridePath);
193       }
194     else if(const char* rulesOverridePath2 =
195             this->Makefile->GetDefinition(rulesOverrideBase))
196       {
197       fprintf(fout, "SET(%s \"%s\")\n",
198               rulesOverrideBase, rulesOverridePath2);
199       }
200
201     if(lang)
202       {
203       fprintf(fout, "PROJECT(CMAKE_TRY_COMPILE %s)\n", lang);
204       }
205     else
206       {
207       fclose(fout);
208       cmOStringStream err;
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)
218         {
219         err << " " << *l;
220         }
221       err << "\nSee project() command to enable other languages.";
222       this->Makefile->IssueMessage(cmake::FATAL_ERROR, err.str());
223       return -1;
224       }
225     std::string langFlags = "CMAKE_";
226     langFlags +=  lang;
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());
231     if(flags)
232       {
233       fprintf(fout, " %s ", flags);
234       }
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())
241       {
242       fprintf(fout, "ADD_DEFINITIONS( ");
243       for (i = 0; i < compileFlags.size(); ++i)
244         {
245         fprintf(fout,"%s ",compileFlags[i].c_str());
246         }
247       fprintf(fout, ")\n");
248       }
249
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.
256       */
257     if(this->Makefile->GetDefinition("CMAKE_TRY_COMPILE_OSX_ARCHITECTURES")!=0)
258       {
259       std::string flag="-DCMAKE_OSX_ARCHITECTURES=";
260       flag += this->Makefile->GetSafeDefinition(
261                                         "CMAKE_TRY_COMPILE_OSX_ARCHITECTURES");
262       cmakeFlags.push_back(flag);
263       }
264     else if (this->Makefile->GetDefinition("CMAKE_OSX_ARCHITECTURES")!=0)
265       {
266       std::string flag="-DCMAKE_OSX_ARCHITECTURES=";
267       flag += this->Makefile->GetSafeDefinition("CMAKE_OSX_ARCHITECTURES");
268       cmakeFlags.push_back(flag);
269       }
270     /* on APPLE also pass CMAKE_OSX_SYSROOT to the try_compile */
271     if(this->Makefile->GetDefinition("CMAKE_OSX_SYSROOT")!=0)
272       {
273       std::string flag="-DCMAKE_OSX_SYSROOT=";
274       flag += this->Makefile->GetSafeDefinition("CMAKE_OSX_SYSROOT");
275       cmakeFlags.push_back(flag);
276       }
277     /* on APPLE also pass CMAKE_OSX_DEPLOYMENT_TARGET to the try_compile */
278     if(this->Makefile->GetDefinition("CMAKE_OSX_DEPLOYMENT_TARGET")!=0)
279       {
280       std::string flag="-DCMAKE_OSX_DEPLOYMENT_TARGET=";
281       flag += this->Makefile->GetSafeDefinition("CMAKE_OSX_DEPLOYMENT_TARGET");
282       cmakeFlags.push_back(flag);
283       }
284
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;
290
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);
297     fclose(fout);
298     projectName = "CMAKE_TRY_COMPILE";
299     // if the source is not in CMakeTmp
300     if(source.find("CMakeTmp") == source.npos)
301       {
302       this->Makefile->AddCMakeDependFile(source.c_str());
303       }
304
305     }
306   // else the srcdir bindir project target signature
307   else
308     {
309     projectName = argv[3].c_str();
310
311     if (argv.size() - extraArgs == 5)
312       {
313       targetName = argv[4].c_str();
314       }
315     }
316
317   bool erroroc = cmSystemTools::GetErrorOccuredFlag();
318   cmSystemTools::ResetErrorOccuredFlag();
319   std::string output;
320   // actually do the try compile now that everything is setup
321   int res = this->Makefile->TryCompile(sourceDirectory,
322                                        this->BinaryDirectory.c_str(),
323                                        projectName,
324                                        targetName,
325                                        this->SrcFileSignature,
326                                        &cmakeFlags,
327                                        &output);
328   if ( erroroc )
329     {
330     cmSystemTools::SetErrorOccured();
331     }
332
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);
338
339   if ( outputVariable.size() > 0 )
340     {
341     this->Makefile->AddDefinition(outputVariable.c_str(), output.c_str());
342     }
343
344   if (this->SrcFileSignature)
345     {
346     this->FindOutputFile(targetName);
347
348     if ((res==0) && (copyFile.size()))
349       {
350       if(this->OutputFile.empty() ||
351          !cmSystemTools::CopyFileAlways(this->OutputFile.c_str(),
352                                         copyFile.c_str()))
353         {
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())
360           {
361           emsg << this->FindErrorMessage.c_str();
362           }
363         this->Makefile->IssueMessage(cmake::FATAL_ERROR, emsg.str());
364         return -1;
365         }
366       }
367     }
368   return res;
369 }
370
371 void cmCoreTryCompile::CleanupFiles(const char* binDir)
372 {
373   if ( !binDir )
374     {
375     return;
376     }
377
378   std::string bdir = binDir;
379   if(bdir.find("CMakeTmp") == std::string::npos)
380     {
381     cmSystemTools::Error(
382       "TRY_COMPILE attempt to remove -rf directory that does not contain "
383       "CMakeTmp:", binDir);
384     return;
385     }
386
387   cmsys::Directory dir;
388   dir.Load(binDir);
389   size_t fileNum;
390   std::set<cmStdString> deletedFiles;
391   for (fileNum = 0; fileNum <  dir.GetNumberOfFiles(); ++fileNum)
392     {
393     if (strcmp(dir.GetFile(static_cast<unsigned long>(fileNum)),".") &&
394         strcmp(dir.GetFile(static_cast<unsigned long>(fileNum)),".."))
395       {
396
397       if(deletedFiles.find( dir.GetFile(static_cast<unsigned long>(fileNum)))
398          == deletedFiles.end())
399         {
400         deletedFiles.insert(dir.GetFile(static_cast<unsigned long>(fileNum)));
401         std::string fullPath = binDir;
402         fullPath += "/";
403         fullPath += dir.GetFile(static_cast<unsigned long>(fileNum));
404         if(cmSystemTools::FileIsDirectory(fullPath.c_str()))
405           {
406           this->CleanupFiles(fullPath.c_str());
407           }
408         else
409           {
410           // Sometimes anti-virus software hangs on to new files so we
411           // cannot delete them immediately.  Try a few times.
412           int tries = 5;
413           while(!cmSystemTools::RemoveFile(fullPath.c_str()) &&
414                 --tries && cmSystemTools::FileExists(fullPath.c_str()))
415             {
416             cmSystemTools::Delay(500);
417             }
418           if(tries == 0)
419             {
420             std::string m = "Remove failed on file: " + fullPath;
421             cmSystemTools::ReportLastSystemError(m.c_str());
422             }
423           }
424         }
425       }
426     }
427 }
428
429 void cmCoreTryCompile::FindOutputFile(const char* targetName)
430 {
431   this->FindErrorMessage = "";
432   this->OutputFile = "";
433   std::string tmpOutputFile = "/";
434   tmpOutputFile += targetName;
435   tmpOutputFile +=this->Makefile->GetSafeDefinition("CMAKE_EXECUTABLE_SUFFIX");
436
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("");
441
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])
446     {
447     std::string tmp = "/";
448     tmp += config;
449     searchDirs.push_back(tmp);
450     }
451   searchDirs.push_back("/Debug");
452   searchDirs.push_back("/Development");
453
454   for(std::vector<std::string>::const_iterator it = searchDirs.begin();
455       it != searchDirs.end();
456       ++it)
457     {
458     std::string command = this->BinaryDirectory;
459     command += *it;
460     command += tmpOutputFile;
461     if(cmSystemTools::FileExists(command.c_str()))
462       {
463       tmpOutputFile = cmSystemTools::CollapseFullPath(command.c_str());
464       this->OutputFile = tmpOutputFile;
465       return;
466       }
467     }
468
469   cmOStringStream emsg;
470   emsg << "Unable to find the executable at any of:\n";
471   for (unsigned int i = 0; i < searchDirs.size(); ++i)
472     {
473     emsg << "  " << this->BinaryDirectory << searchDirs[i]
474          << tmpOutputFile << "\n";
475     }
476   this->FindErrorMessage = emsg.str();
477   return;
478 }