1 /*============================================================================
2 CMake - Cross Platform Makefile Generator
3 Copyright 2000-2009 Kitware, Inc., Insight Software Consortium
5 Distributed under the OSI-approved BSD License (the "License");
6 see accompanying file Copyright.txt for details.
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 "cmTryRunCommand.h"
13 #include "cmCacheManager.h"
14 #include "cmTryCompileCommand.h"
18 ::InitialPass(std::vector<std::string> const& argv, cmExecutionStatus &)
25 if(this->Makefile->GetCMakeInstance()->GetWorkingMode() ==
26 cmake::FIND_PACKAGE_MODE)
28 this->Makefile->IssueMessage(cmake::FATAL_ERROR,
29 "The TRY_RUN() command is not supported in --find-package mode.");
33 // build an arg list for TryCompile and extract the runArgs,
34 std::vector<std::string> tryCompile;
36 this->CompileResultVariable = "";
37 this->RunResultVariable = "";
38 this->OutputVariable = "";
39 this->RunOutputVariable = "";
40 this->CompileOutputVariable = "";
44 for (i = 1; i < argv.size(); ++i)
46 if (argv[i] == "ARGS")
49 while (i < argv.size() && argv[i] != "COMPILE_DEFINITIONS" &&
50 argv[i] != "CMAKE_FLAGS")
58 tryCompile.push_back(argv[i]);
63 if (argv[i] == "OUTPUT_VARIABLE")
65 if ( argv.size() <= (i+1) )
68 "OUTPUT_VARIABLE specified but there is no variable");
72 this->OutputVariable = argv[i];
74 else if (argv[i] == "RUN_OUTPUT_VARIABLE")
76 if (argv.size() <= (i + 1))
79 "RUN_OUTPUT_VARIABLE specified but there is no variable");
83 this->RunOutputVariable = argv[i];
85 else if (argv[i] == "COMPILE_OUTPUT_VARIABLE")
87 if (argv.size() <= (i + 1))
90 "COMPILE_OUTPUT_VARIABLE specified but there is no variable");
94 this->CompileOutputVariable = argv[i];
98 tryCompile.push_back(argv[i]);
103 // although they could be used together, don't allow it, because
104 // using OUTPUT_VARIABLE makes crosscompiling harder
105 if (this->OutputVariable.size()
106 && ((this->RunOutputVariable.size())
107 || (this->CompileOutputVariable.size())))
109 cmSystemTools::Error(
110 "You cannot use OUTPUT_VARIABLE together with COMPILE_OUTPUT_VARIABLE "
111 "or RUN_OUTPUT_VARIABLE. Please use only COMPILE_OUTPUT_VARIABLE and/or "
112 "RUN_OUTPUT_VARIABLE.");
116 bool captureRunOutput = false;
117 if (this->OutputVariable.size())
119 captureRunOutput = true;
120 tryCompile.push_back("OUTPUT_VARIABLE");
121 tryCompile.push_back(this->OutputVariable);
123 if (this->CompileOutputVariable.size())
125 tryCompile.push_back("OUTPUT_VARIABLE");
126 tryCompile.push_back(this->CompileOutputVariable);
128 if (this->RunOutputVariable.size())
130 captureRunOutput = true;
133 this->RunResultVariable = argv[0];
134 this->CompileResultVariable = argv[1];
136 // do the try compile
137 int res = this->TryCompileCode(tryCompile);
139 // now try running the command if it compiled
142 if (this->OutputFile.size() == 0)
144 cmSystemTools::Error(this->FindErrorMessage.c_str());
148 // "run" it and capture the output
149 std::string runOutputContents;
150 if (this->Makefile->IsOn("CMAKE_CROSSCOMPILING"))
152 this->DoNotRunExecutable(runArgs,
154 captureRunOutput ? &runOutputContents : 0);
158 this->RunExecutable(runArgs, &runOutputContents);
161 // now put the output into the variables
162 if(this->RunOutputVariable.size())
164 this->Makefile->AddDefinition(this->RunOutputVariable.c_str(),
165 runOutputContents.c_str());
168 if(this->OutputVariable.size())
170 // if the TryCompileCore saved output in this outputVariable then
171 // prepend that output to this output
172 const char* compileOutput
173 = this->Makefile->GetDefinition(this->OutputVariable.c_str());
176 runOutputContents = std::string(compileOutput) + runOutputContents;
178 this->Makefile->AddDefinition(this->OutputVariable.c_str(),
179 runOutputContents.c_str());
184 // if we created a directory etc, then cleanup after ourselves
185 if(!this->Makefile->GetCMakeInstance()->GetDebugTryCompile())
187 this->CleanupFiles(this->BinaryDirectory.c_str());
192 void cmTryRunCommand::RunExecutable(const std::string& runArgs,
196 std::string finalCommand = cmSystemTools::ConvertToRunCommandPath(
197 this->OutputFile.c_str());
200 finalCommand += runArgs;
203 bool worked = cmSystemTools::RunSingleCommand(finalCommand.c_str(),
205 0, cmSystemTools::OUTPUT_NONE, timeout);
210 sprintf(retChar, "%i", retVal);
214 strcpy(retChar, "FAILED_TO_RUN");
216 this->Makefile->AddCacheDefinition(this->RunResultVariable.c_str(), retChar,
218 cmCacheManager::INTERNAL);
221 /* This is only used when cross compiling. Instead of running the
222 executable, two cache variables are created which will hold the results
223 the executable would have produced.
225 void cmTryRunCommand::DoNotRunExecutable(const std::string& runArgs,
226 const std::string& srcFile,
230 // copy the executable out of the CMakeFiles/ directory, so it is not
231 // removed at the end of TRY_RUN and the user can run it manually
232 // on the target platform.
233 std::string copyDest = this->Makefile->GetHomeOutputDirectory();
234 copyDest += cmake::GetCMakeFilesDirectory();
236 copyDest += cmSystemTools::GetFilenameWithoutExtension(
237 this->OutputFile.c_str());
239 copyDest += this->RunResultVariable;
240 copyDest += cmSystemTools::GetFilenameExtension(this->OutputFile.c_str());
241 cmSystemTools::CopyFileAlways(this->OutputFile.c_str(), copyDest.c_str());
243 std::string resultFileName = this->Makefile->GetHomeOutputDirectory();
244 resultFileName += "/TryRunResults.cmake";
246 std::string detailsString = "For details see ";
247 detailsString += resultFileName;
249 std::string internalRunOutputName=this->RunResultVariable+"__TRYRUN_OUTPUT";
252 if (this->Makefile->GetDefinition(this->RunResultVariable.c_str()) == 0)
254 // if the variables doesn't exist, create it with a helpful error text
255 // and mark it as advanced
257 comment += "Run result of TRY_RUN(), indicates whether the executable "
258 "would have been able to run on its target platform.\n";
259 comment += detailsString;
260 this->Makefile->AddCacheDefinition(this->RunResultVariable.c_str(),
261 "PLEASE_FILL_OUT-FAILED_TO_RUN",
263 cmCacheManager::STRING);
265 cmCacheManager::CacheIterator it = this->Makefile->GetCacheManager()->
266 GetCacheIterator(this->RunResultVariable.c_str());
269 it.SetProperty("ADVANCED", "1");
275 // is the output from the executable used ?
278 if (this->Makefile->GetDefinition(internalRunOutputName.c_str()) == 0)
280 // if the variables doesn't exist, create it with a helpful error text
281 // and mark it as advanced
283 comment+="Output of TRY_RUN(), contains the text, which the executable "
284 "would have printed on stdout and stderr on its target platform.\n";
285 comment += detailsString;
287 this->Makefile->AddCacheDefinition(internalRunOutputName.c_str(),
288 "PLEASE_FILL_OUT-NOTFOUND",
290 cmCacheManager::STRING);
291 cmCacheManager::CacheIterator it = this->Makefile->GetCacheManager()->
292 GetCacheIterator(internalRunOutputName.c_str());
295 it.SetProperty("ADVANCED", "1");
304 static bool firstTryRun = true;
305 std::ofstream file(resultFileName.c_str(),
306 firstTryRun ? std::ios::out : std::ios::app);
311 file << "# This file was generated by CMake because it detected "
312 "TRY_RUN() commands\n"
313 "# in crosscompiling mode. It will be overwritten by the next "
315 "# Copy it to a safe location, set the variables to "
316 "appropriate values\n"
317 "# and use it then to preset the CMake cache (using -C).\n\n";
320 std::string comment ="\n";
321 comment += this->RunResultVariable;
322 comment += "\n indicates whether the executable would have been able "
324 " target platform. If so, set ";
325 comment += this->RunResultVariable;
327 " the exit code (in many cases 0 for success), otherwise "
328 "enter \"FAILED_TO_RUN\".\n";
331 comment += internalRunOutputName;
332 comment += "\n contains the text the executable "
333 "would have printed on stdout and stderr.\n"
334 " If the executable would not have been able to run, set ";
335 comment += internalRunOutputName;
336 comment += " empty.\n"
337 " Otherwise check if the output is evaluated by the "
338 "calling CMake code. If so,\n"
339 " check what the source file would have printed when "
340 "called with the given arguments.\n";
343 comment += this->CompileResultVariable;
344 comment += " variable holds the build result for this TRY_RUN().\n\n"
346 comment += srcFile + "\n";
347 comment += "Executable : ";
348 comment += copyDest + "\n";
349 comment += "Run arguments : ";
352 comment += " Called from: " + this->Makefile->GetListFileStack();
353 cmsys::SystemTools::ReplaceString(comment, "\n", "\n# ");
354 file << comment << "\n\n";
356 file << "SET( " << this->RunResultVariable << " \n \""
357 << this->Makefile->GetDefinition(this->RunResultVariable.c_str())
358 << "\"\n CACHE STRING \"Result from TRY_RUN\" FORCE)\n\n";
362 file << "SET( " << internalRunOutputName << " \n \""
363 << this->Makefile->GetDefinition(internalRunOutputName.c_str())
364 << "\"\n CACHE STRING \"Output from TRY_RUN\" FORCE)\n\n";
370 std::string errorMessage = "TRY_RUN() invoked in cross-compiling mode, "
371 "please set the following cache variables "
373 errorMessage += " " + this->RunResultVariable + " (advanced)\n";
376 errorMessage += " " + internalRunOutputName + " (advanced)\n";
378 errorMessage += detailsString;
379 cmSystemTools::Error(errorMessage.c_str());
385 (*out) = this->Makefile->GetDefinition(internalRunOutputName.c_str());