ENH: Return utility target after creation
[cmake.git] / Source / cmTryRunCommand.cxx
blob0afca8035c217dc654e6d46cda296c76f632d794
1 /*=========================================================================
3 Program: CMake - Cross-Platform Makefile Generator
4 Module: $RCSfile: cmTryRunCommand.cxx,v $
5 Language: C++
6 Date: $Date: 2008-01-23 15:27:59 $
7 Version: $Revision: 1.41 $
9 Copyright (c) 2002 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 "cmTryRunCommand.h"
18 #include "cmCacheManager.h"
19 #include "cmTryCompileCommand.h"
21 // cmTryRunCommand
22 bool cmTryRunCommand
23 ::InitialPass(std::vector<std::string> const& argv, cmExecutionStatus &)
25 if(argv.size() < 4)
27 return false;
30 // build an arg list for TryCompile and extract the runArgs,
31 std::vector<std::string> tryCompile;
33 this->CompileResultVariable = "";
34 this->RunResultVariable = "";
35 this->OutputVariable = "";
36 this->RunOutputVariable = "";
37 this->CompileOutputVariable = "";
39 std::string runArgs;
40 unsigned int i;
41 for (i = 1; i < argv.size(); ++i)
43 if (argv[i] == "ARGS")
45 ++i;
46 while (i < argv.size() && argv[i] != "COMPILE_DEFINITIONS" &&
47 argv[i] != "CMAKE_FLAGS")
49 runArgs += " ";
50 runArgs += argv[i];
51 ++i;
53 if (i < argv.size())
55 tryCompile.push_back(argv[i]);
58 else
60 if (argv[i] == "OUTPUT_VARIABLE")
62 if ( argv.size() <= (i+1) )
64 cmSystemTools::Error(
65 "OUTPUT_VARIABLE specified but there is no variable");
66 return false;
68 i++;
69 this->OutputVariable = argv[i];
71 else if (argv[i] == "RUN_OUTPUT_VARIABLE")
73 if (argv.size() <= (i + 1))
75 cmSystemTools::Error(
76 "RUN_OUTPUT_VARIABLE specified but there is no variable");
77 return false;
79 i++;
80 this->RunOutputVariable = argv[i];
82 else if (argv[i] == "COMPILE_OUTPUT_VARIABLE")
84 if (argv.size() <= (i + 1))
86 cmSystemTools::Error(
87 "COMPILE_OUTPUT_VARIABLE specified but there is no variable");
88 return false;
90 i++;
91 this->CompileOutputVariable = argv[i];
93 else
95 tryCompile.push_back(argv[i]);
100 // although they could be used together, don't allow it, because
101 // using OUTPUT_VARIABLE makes crosscompiling harder
102 if (this->OutputVariable.size()
103 && ((this->RunOutputVariable.size())
104 || (this->CompileOutputVariable.size())))
106 cmSystemTools::Error(
107 "You cannot use OUTPUT_VARIABLE together with COMPILE_OUTPUT_VARIABLE "
108 "or RUN_OUTPUT_VARIABLE. Please use only COMPILE_OUTPUT_VARIABLE and/or "
109 "RUN_OUTPUT_VARIABLE.");
110 return false;
113 bool captureRunOutput = false;
114 if (this->OutputVariable.size())
116 captureRunOutput = true;
117 tryCompile.push_back("OUTPUT_VARIABLE");
118 tryCompile.push_back(this->OutputVariable);
120 if (this->CompileOutputVariable.size())
122 tryCompile.push_back("OUTPUT_VARIABLE");
123 tryCompile.push_back(this->CompileOutputVariable);
125 if (this->RunOutputVariable.size())
127 captureRunOutput = true;
130 this->RunResultVariable = argv[0];
131 this->CompileResultVariable = argv[1];
133 // do the try compile
134 int res = this->TryCompileCode(tryCompile);
136 // now try running the command if it compiled
137 if (!res)
139 if (this->OutputFile.size() == 0)
141 cmSystemTools::Error(this->FindErrorMessage.c_str());
143 else
145 // "run" it and capture the output
146 std::string runOutputContents;
147 if (this->Makefile->IsOn("CMAKE_CROSSCOMPILING"))
149 this->DoNotRunExecutable(runArgs,
150 argv[3],
151 captureRunOutput ? &runOutputContents : 0);
153 else
155 this->RunExecutable(runArgs, &runOutputContents);
158 // now put the output into the variables
159 if(this->RunOutputVariable.size())
161 this->Makefile->AddDefinition(this->RunOutputVariable.c_str(),
162 runOutputContents.c_str());
165 if(this->OutputVariable.size())
167 // if the TryCompileCore saved output in this outputVariable then
168 // prepend that output to this output
169 const char* compileOutput
170 = this->Makefile->GetDefinition(this->OutputVariable.c_str());
171 if (compileOutput)
173 runOutputContents = std::string(compileOutput) + runOutputContents;
175 this->Makefile->AddDefinition(this->OutputVariable.c_str(),
176 runOutputContents.c_str());
181 // if we created a directory etc, then cleanup after ourselves
182 if(!this->Makefile->GetCMakeInstance()->GetDebugTryCompile())
184 this->CleanupFiles(this->BinaryDirectory.c_str());
186 return true;
189 void cmTryRunCommand::RunExecutable(const std::string& runArgs,
190 std::string* out)
192 int retVal = -1;
193 std::string finalCommand = cmSystemTools::ConvertToRunCommandPath(
194 this->OutputFile.c_str());
195 if (runArgs.size())
197 finalCommand += runArgs;
199 int timeout = 0;
200 bool worked = cmSystemTools::RunSingleCommand(finalCommand.c_str(),
201 out, &retVal,
202 0, false, timeout);
203 // set the run var
204 char retChar[1000];
205 if (worked)
207 sprintf(retChar, "%i", retVal);
209 else
211 strcpy(retChar, "FAILED_TO_RUN");
213 this->Makefile->AddCacheDefinition(this->RunResultVariable.c_str(), retChar,
214 "Result of TRY_RUN",
215 cmCacheManager::INTERNAL);
218 /* This is only used when cross compiling. Instead of running the
219 executable, two cache variables are created which will hold the results
220 the executable would have produced.
222 void cmTryRunCommand::DoNotRunExecutable(const std::string& runArgs,
223 const std::string& srcFile,
224 std::string* out
227 // copy the executable out of the CMakeFiles/ directory, so it is not
228 // removed at the end of TRY_RUN and the user can run it manually
229 // on the target platform.
230 std::string copyDest = this->Makefile->GetHomeOutputDirectory();
231 copyDest += cmake::GetCMakeFilesDirectory();
232 copyDest += "/";
233 copyDest += cmSystemTools::GetFilenameWithoutExtension(
234 this->OutputFile.c_str());
235 copyDest += "-";
236 copyDest += this->RunResultVariable;
237 copyDest += cmSystemTools::GetFilenameExtension(this->OutputFile.c_str());
238 cmSystemTools::CopyFileAlways(this->OutputFile.c_str(), copyDest.c_str());
240 std::string resultFileName = this->Makefile->GetHomeOutputDirectory();
241 resultFileName += "/TryRunResults.cmake";
243 std::string detailsString = "For details see ";
244 detailsString += resultFileName;
246 std::string internalRunOutputName=this->RunResultVariable+"__TRYRUN_OUTPUT";
247 bool error = false;
249 if (this->Makefile->GetDefinition(this->RunResultVariable.c_str()) == 0)
251 // if the variables doesn't exist, create it with a helpful error text
252 // and mark it as advanced
253 std::string comment;
254 comment += "Run result of TRY_RUN(), indicates whether the executable "
255 "would have been able to run on its target platform.\n";
256 comment += detailsString;
257 this->Makefile->AddCacheDefinition(this->RunResultVariable.c_str(),
258 "PLEASE_FILL_OUT-FAILED_TO_RUN",
259 comment.c_str(),
260 cmCacheManager::STRING);
262 cmCacheManager::CacheIterator it = this->Makefile->GetCacheManager()->
263 GetCacheIterator(this->RunResultVariable.c_str());
264 if ( !it.IsAtEnd() )
266 it.SetProperty("ADVANCED", "1");
269 error = true;
272 // is the output from the executable used ?
273 if (out!=0)
275 if (this->Makefile->GetDefinition(internalRunOutputName.c_str()) == 0)
277 // if the variables doesn't exist, create it with a helpful error text
278 // and mark it as advanced
279 std::string comment;
280 comment+="Output of TRY_RUN(), contains the text, which the executable "
281 "would have printed on stdout and stderr on its target platform.\n";
282 comment += detailsString;
284 this->Makefile->AddCacheDefinition(internalRunOutputName.c_str(),
285 "PLEASE_FILL_OUT-NOTFOUND",
286 comment.c_str(),
287 cmCacheManager::STRING);
288 cmCacheManager::CacheIterator it = this->Makefile->GetCacheManager()->
289 GetCacheIterator(internalRunOutputName.c_str());
290 if ( !it.IsAtEnd() )
292 it.SetProperty("ADVANCED", "1");
295 error = true;
299 if (error)
301 static bool firstTryRun = true;
302 std::ofstream file(resultFileName.c_str(),
303 firstTryRun ? std::ios::out : std::ios::app);
304 if ( file )
306 if (firstTryRun)
308 file << "# This file was generated by CMake because it detected "
309 "TRY_RUN() commands\n"
310 "# in crosscompiling mode. It will be overwritten by the next "
311 "CMake run.\n"
312 "# Copy it to a safe location, set the variables to "
313 "appropriate values\n"
314 "# and use it then to preset the CMake cache (using -C).\n\n";
317 std::string comment ="\n";
318 comment += this->RunResultVariable;
319 comment += "\n indicates whether the executable would have been able "
320 "to run on its\n"
321 " target platform. If so, set ";
322 comment += this->RunResultVariable;
323 comment += " to\n"
324 " the exit code (in many cases 0 for success), otherwise "
325 "enter \"FAILED_TO_RUN\".\n";
326 if (out!=0)
328 comment += internalRunOutputName;
329 comment += "\n contains the text the executable "
330 "would have printed on stdout and stderr.\n"
331 " If the executable would not have been able to run, set ";
332 comment += internalRunOutputName;
333 comment += " empty.\n"
334 " Otherwise check if the output is evaluated by the "
335 "calling CMake code. If so,\n"
336 " check what the source file would have printed when "
337 "called with the given arguments.\n";
339 comment += "The ";
340 comment += this->CompileResultVariable;
341 comment += " variable holds the build result for this TRY_RUN().\n\n"
342 "Source file : ";
343 comment += srcFile + "\n";
344 comment += "Executable : ";
345 comment += copyDest + "\n";
346 comment += "Run arguments : ";
347 comment += runArgs;
348 comment += "\n";
349 comment += " Called from: " + this->Makefile->GetListFileStack();
350 cmsys::SystemTools::ReplaceString(comment, "\n", "\n# ");
351 file << comment << "\n\n";
353 file << "SET( " << this->RunResultVariable << " \n \""
354 << this->Makefile->GetDefinition(this->RunResultVariable.c_str())
355 << "\"\n CACHE STRING \"Result from TRY_RUN\" FORCE)\n\n";
357 if (out!=0)
359 file << "SET( " << internalRunOutputName << " \n \""
360 << this->Makefile->GetDefinition(internalRunOutputName.c_str())
361 << "\"\n CACHE STRING \"Output from TRY_RUN\" FORCE)\n\n";
363 file.close();
365 firstTryRun = false;
367 std::string errorMessage = "TRY_RUN() invoked in cross-compiling mode, "
368 "please set the following cache variables "
369 "appropriatly:\n";
370 errorMessage += " " + this->RunResultVariable + " (advanced)\n";
371 if (out!=0)
373 errorMessage += " " + internalRunOutputName + " (advanced)\n";
375 errorMessage += detailsString;
376 cmSystemTools::Error(errorMessage.c_str());
377 return;
380 if (out!=0)
382 (*out) = this->Makefile->GetDefinition(internalRunOutputName.c_str());