1 /*=========================================================================
3 Program: CMake - Cross-Platform Makefile Generator
4 Module: $RCSfile: cmCoreTryCompile.cxx,v $
6 Date: $Date: 2008-03-26 17:50:23 $
7 Version: $Revision: 1.10 $
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"
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;
32 const char* sourceDirectory
= argv
[2].c_str();
33 const char* projectName
= 0;
34 const char* targetName
= 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";
51 cmakeFlags
.push_back(argv
[i
]);
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) )
66 "OUTPUT_VARIABLE specified but there is no variable");
70 outputVariable
= argv
[i
+1];
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")
82 for (i
= i
+ 1; i
< argv
.size() && argv
[i
] != "CMAKE_FLAGS" &&
83 argv
[i
] != "OUTPUT_VARIABLE";
87 compileFlags
.push_back(argv
[i
]);
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");
106 copyFile
= argv
[i
+1];
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
119 if (this->SrcFileSignature
)
121 this->BinaryDirectory
+= cmake::GetCMakeFilesDirectory();
122 this->BinaryDirectory
+= "/CMakeTmp";
126 // only valid for srcfile signatures
127 if (compileFlags
.size())
129 cmSystemTools::Error(
130 "COMPILE_FLAGS specified on a srcdir type TRY_COMPILE");
135 cmSystemTools::Error("COPY_FILE specified on a srcdir type TRY_COMPILE");
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());
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");
167 cmSystemTools::Error("Failed to create CMakeList file for ",
168 outFileName
.c_str());
169 cmSystemTools::ReportLastSystemError("");
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_minimum_required(VERSION %u.%u)\n",
179 cmVersion::GetMajorVersion(), cmVersion::GetMinorVersion());
182 fprintf(fout
, "SET(CMAKE_MODULE_PATH %s)\n", def
);
186 fprintf(fout
, "PROJECT(CMAKE_TRY_COMPILE %s)\n", lang
);
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
)
202 err
<< "\nSee PROJECT command for help enabling other languages.";
203 cmSystemTools::Error(err
.str().c_str());
207 std::string langFlags
= "CMAKE_";
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());
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());
257 "TARGET_LINK_LIBRARIES(cmTryCompileExec ${LINK_LIBRARIES})\n");
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
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();
282 // actually do the try compile now that everything is setup
283 int res
= this->Makefile
->TryCompile(sourceDirectory
,
284 this->BinaryDirectory
.c_str(),
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(),
313 cmSystemTools::Error("Could not COPY_FILE");
321 void cmCoreTryCompile::CleanupFiles(const char* binDir
)
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
);
337 cmsys::Directory dir
;
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
;
353 fullPath
+= dir
.GetFile(static_cast<unsigned long>(fileNum
));
354 if(cmSystemTools::FileIsDirectory(fullPath
.c_str()))
356 this->CleanupFiles(fullPath
.c_str());
360 if(!cmSystemTools::RemoveFile(fullPath
.c_str()))
362 bool removed
= false;
364 // sometimes anti-virus software hangs on to
365 // new files and we can not delete them, so try
366 // 5 times with .5 second delay between tries.
367 while(!removed
&& numAttempts
< 5)
369 cmSystemTools::Delay(500);
370 if(cmSystemTools::RemoveFile(fullPath
.c_str()))
378 std::string m
= "Remove failed on file: ";
380 cmSystemTools::ReportLastSystemError(m
.c_str());
389 void cmCoreTryCompile::FindOutputFile(const char* targetName
)
391 this->FindErrorMessage
= "";
392 this->OutputFile
= "";
393 std::string tmpOutputFile
= "/";
394 tmpOutputFile
+= targetName
;
395 tmpOutputFile
+=this->Makefile
->GetSafeDefinition("CMAKE_EXECUTABLE_SUFFIX");
397 // a list of directories where to search for the compilation result
398 // at first directly in the binary dir
399 std::vector
<std::string
> searchDirs
;
400 searchDirs
.push_back("");
402 const char* config
= this->Makefile
->GetDefinition(
403 "CMAKE_TRY_COMPILE_CONFIGURATION");
404 // if a config was specified try that first
405 if (config
&& config
[0])
407 std::string tmp
= "/";
409 searchDirs
.push_back(tmp
);
411 searchDirs
.push_back("/Debug");
412 searchDirs
.push_back("/Development");
414 for(std::vector
<std::string
>::const_iterator it
= searchDirs
.begin();
415 it
!= searchDirs
.end();
418 std::string command
= this->BinaryDirectory
;
420 command
+= tmpOutputFile
;
421 if(cmSystemTools::FileExists(command
.c_str()))
423 tmpOutputFile
= cmSystemTools::CollapseFullPath(command
.c_str());
424 this->OutputFile
= tmpOutputFile
;
429 cmOStringStream emsg
;
430 emsg
<< "Unable to find executable for " << this->GetName() << ": tried \"";
431 for (unsigned int i
= 0; i
< searchDirs
.size(); ++i
)
433 emsg
<< this->BinaryDirectory
<< searchDirs
[i
] << tmpOutputFile
;
434 if (i
< searchDirs
.size() - 1)
443 this->FindErrorMessage
= emsg
.str();