1 /*=========================================================================
3 Program: CMake - Cross-Platform Makefile Generator
4 Module: $RCSfile: cmTryRunCommand.cxx,v $
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"
23 ::InitialPass(std::vector
<std::string
> const& argv
, cmExecutionStatus
&)
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
= "";
41 for (i
= 1; i
< argv
.size(); ++i
)
43 if (argv
[i
] == "ARGS")
46 while (i
< argv
.size() && argv
[i
] != "COMPILE_DEFINITIONS" &&
47 argv
[i
] != "CMAKE_FLAGS")
55 tryCompile
.push_back(argv
[i
]);
60 if (argv
[i
] == "OUTPUT_VARIABLE")
62 if ( argv
.size() <= (i
+1) )
65 "OUTPUT_VARIABLE specified but there is no variable");
69 this->OutputVariable
= argv
[i
];
71 else if (argv
[i
] == "RUN_OUTPUT_VARIABLE")
73 if (argv
.size() <= (i
+ 1))
76 "RUN_OUTPUT_VARIABLE specified but there is no variable");
80 this->RunOutputVariable
= argv
[i
];
82 else if (argv
[i
] == "COMPILE_OUTPUT_VARIABLE")
84 if (argv
.size() <= (i
+ 1))
87 "COMPILE_OUTPUT_VARIABLE specified but there is no variable");
91 this->CompileOutputVariable
= argv
[i
];
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.");
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
139 if (this->OutputFile
.size() == 0)
141 cmSystemTools::Error(this->FindErrorMessage
.c_str());
145 // "run" it and capture the output
146 std::string runOutputContents
;
147 if (this->Makefile
->IsOn("CMAKE_CROSSCOMPILING"))
149 this->DoNotRunExecutable(runArgs
,
151 captureRunOutput
? &runOutputContents
: 0);
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());
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());
189 void cmTryRunCommand::RunExecutable(const std::string
& runArgs
,
193 std::string finalCommand
= cmSystemTools::ConvertToRunCommandPath(
194 this->OutputFile
.c_str());
197 finalCommand
+= runArgs
;
200 bool worked
= cmSystemTools::RunSingleCommand(finalCommand
.c_str(),
207 sprintf(retChar
, "%i", retVal
);
211 strcpy(retChar
, "FAILED_TO_RUN");
213 this->Makefile
->AddCacheDefinition(this->RunResultVariable
.c_str(), retChar
,
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
,
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();
233 copyDest
+= cmSystemTools::GetFilenameWithoutExtension(
234 this->OutputFile
.c_str());
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";
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
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",
260 cmCacheManager::STRING
);
262 cmCacheManager::CacheIterator it
= this->Makefile
->GetCacheManager()->
263 GetCacheIterator(this->RunResultVariable
.c_str());
266 it
.SetProperty("ADVANCED", "1");
272 // is the output from the executable used ?
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
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",
287 cmCacheManager::STRING
);
288 cmCacheManager::CacheIterator it
= this->Makefile
->GetCacheManager()->
289 GetCacheIterator(internalRunOutputName
.c_str());
292 it
.SetProperty("ADVANCED", "1");
301 static bool firstTryRun
= true;
302 std::ofstream
file(resultFileName
.c_str(),
303 firstTryRun
? std::ios::out
: std::ios::app
);
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 "
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 "
321 " target platform. If so, set ";
322 comment
+= this->RunResultVariable
;
324 " the exit code (in many cases 0 for success), otherwise "
325 "enter \"FAILED_TO_RUN\".\n";
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";
340 comment
+= this->CompileResultVariable
;
341 comment
+= " variable holds the build result for this TRY_RUN().\n\n"
343 comment
+= srcFile
+ "\n";
344 comment
+= "Executable : ";
345 comment
+= copyDest
+ "\n";
346 comment
+= "Run arguments : ";
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";
359 file
<< "SET( " << internalRunOutputName
<< " \n \""
360 << this->Makefile
->GetDefinition(internalRunOutputName
.c_str())
361 << "\"\n CACHE STRING \"Output from TRY_RUN\" FORCE)\n\n";
367 std::string errorMessage
= "TRY_RUN() invoked in cross-compiling mode, "
368 "please set the following cache variables "
370 errorMessage
+= " " + this->RunResultVariable
+ " (advanced)\n";
373 errorMessage
+= " " + internalRunOutputName
+ " (advanced)\n";
375 errorMessage
+= detailsString
;
376 cmSystemTools::Error(errorMessage
.c_str());
382 (*out
) = this->Makefile
->GetDefinition(internalRunOutputName
.c_str());