1 /*=========================================================================
3 Program: CMake - Cross-Platform Makefile Generator
4 Module: $RCSfile: cmCoreTryCompile.cxx,v $
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"
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;
31 const char* sourceDirectory
= argv
[2].c_str();
32 const char* projectName
= 0;
33 const char* targetName
= 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";
50 cmakeFlags
.push_back(argv
[i
]);
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) )
65 "OUTPUT_VARIABLE specified but there is no variable");
69 outputVariable
= argv
[i
+1];
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")
81 for (i
= i
+ 1; i
< argv
.size() && argv
[i
] != "CMAKE_FLAGS" &&
82 argv
[i
] != "OUTPUT_VARIABLE";
86 compileFlags
.push_back(argv
[i
]);
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");
105 copyFile
= argv
[i
+1];
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
118 if (this->SrcFileSignature
)
120 this->BinaryDirectory
+= cmake::GetCMakeFilesDirectory();
121 this->BinaryDirectory
+= "/CMakeTmp";
125 // only valid for srcfile signatures
126 if (compileFlags
.size())
128 cmSystemTools::Error(
129 "COMPILE_FLAGS specified on a srcdir type TRY_COMPILE");
134 cmSystemTools::Error("COPY_FILE specified on a srcdir type TRY_COMPILE");
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());
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");
166 cmSystemTools::Error("Failed to create CMakeList file for ",
167 outFileName
.c_str());
168 cmSystemTools::ReportLastSystemError("");
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());
181 fprintf(fout
, "SET(CMAKE_MODULE_PATH %s)\n", def
);
185 fprintf(fout
, "PROJECT(CMAKE_TRY_COMPILE %s)\n", lang
);
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
)
201 err
<< "\nSee PROJECT command for help enabling other languages.";
202 cmSystemTools::Error(err
.str().c_str());
206 std::string langFlags
= "CMAKE_";
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());
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());
256 "TARGET_LINK_LIBRARIES(cmTryCompileExec ${LINK_LIBRARIES})\n");
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
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();
281 // actually do the try compile now that everything is setup
282 int res
= this->Makefile
->TryCompile(sourceDirectory
,
283 this->BinaryDirectory
.c_str(),
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(),
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())
321 emsg
<< this->FindErrorMessage
.c_str() << "\n";
324 cmSystemTools::Error(emsg
.str().c_str());
332 void cmCoreTryCompile::CleanupFiles(const char* binDir
)
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
);
348 cmsys::Directory dir
;
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
;
364 fullPath
+= dir
.GetFile(static_cast<unsigned long>(fileNum
));
365 if(cmSystemTools::FileIsDirectory(fullPath
.c_str()))
367 this->CleanupFiles(fullPath
.c_str());
371 if(!cmSystemTools::RemoveFile(fullPath
.c_str()))
373 bool removed
= false;
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()))
389 std::string m
= "Remove failed on file: ";
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
= "/";
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();
429 std::string command
= this->BinaryDirectory
;
431 command
+= tmpOutputFile
;
432 if(cmSystemTools::FileExists(command
.c_str()))
434 tmpOutputFile
= cmSystemTools::CollapseFullPath(command
.c_str());
435 this->OutputFile
= tmpOutputFile
;
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)
454 this->FindErrorMessage
= emsg
.str();