1 /*=========================================================================
3 Program: CMake - Cross-Platform Makefile Generator
4 Module: $RCSfile: cmCoreTryCompile.cxx,v $
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"
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");
180 fprintf(fout
, "SET(CMAKE_MODULE_PATH %s)\n", def
);
184 fprintf(fout
, "PROJECT(CMAKE_TRY_COMPILE %s)\n", lang
);
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
)
200 err
<< "\nSee PROJECT command for help enabling other languages.";
201 cmSystemTools::Error(err
.str().c_str());
205 std::string langFlags
= "CMAKE_";
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());
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());
255 "TARGET_LINK_LIBRARIES(cmTryCompileExec ${LINK_LIBRARIES})\n");
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
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();
280 // actually do the try compile now that everything is setup
281 int res
= this->Makefile
->TryCompile(sourceDirectory
,
282 this->BinaryDirectory
.c_str(),
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(),
311 cmSystemTools::Error("Could not COPY_FILE");
319 void cmCoreTryCompile::CleanupFiles(const char* binDir
)
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
);
335 cmsys::Directory dir
;
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
;
351 fullPath
+= dir
.GetFile(static_cast<unsigned long>(fileNum
));
352 if(cmSystemTools::FileIsDirectory(fullPath
.c_str()))
354 this->CleanupFiles(fullPath
.c_str());
358 if(!cmSystemTools::RemoveFile(fullPath
.c_str()))
360 std::string m
= "Remove failed on file: ";
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
= "/";
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();
399 std::string command
= this->BinaryDirectory
;
401 command
+= tmpOutputFile
;
402 if(cmSystemTools::FileExists(command
.c_str()))
404 tmpOutputFile
= cmSystemTools::CollapseFullPath(command
.c_str());
405 this->OutputFile
= tmpOutputFile
;
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)
424 this->FindErrorMessage
= emsg
.str();