/src/CMake/Source/cmTryRunCommand.cxx
Line | Count | Source |
1 | | /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying |
2 | | file LICENSE.rst or https://cmake.org/licensing for details. */ |
3 | | #include "cmTryRunCommand.h" |
4 | | |
5 | | #include <stdexcept> |
6 | | |
7 | | #include <cm/optional> |
8 | | #include <cmext/string_view> |
9 | | |
10 | | #include "cmsys/FStream.hxx" |
11 | | |
12 | | #include "cmArgumentParserTypes.h" |
13 | | #include "cmConfigureLog.h" |
14 | | #include "cmCoreTryCompile.h" |
15 | | #include "cmDuration.h" |
16 | | #include "cmExecutionStatus.h" |
17 | | #include "cmList.h" |
18 | | #include "cmMakefile.h" |
19 | | #include "cmMessageType.h" |
20 | | #include "cmRange.h" |
21 | | #include "cmState.h" |
22 | | #include "cmStateTypes.h" |
23 | | #include "cmStringAlgorithms.h" |
24 | | #include "cmSystemTools.h" |
25 | | #include "cmValue.h" |
26 | | #include "cmake.h" |
27 | | |
28 | | namespace { |
29 | | struct cmTryRunResult |
30 | | { |
31 | | bool VariableCached = true; |
32 | | std::string Variable; |
33 | | cm::optional<std::string> Stdout; |
34 | | cm::optional<std::string> Stderr; |
35 | | cm::optional<std::string> ExitCode; |
36 | | }; |
37 | | |
38 | | #ifndef CMAKE_BOOTSTRAP |
39 | | void WriteTryRunEvent(cmConfigureLog& log, cmMakefile const& mf, |
40 | | cmTryCompileResult const& compileResult, |
41 | | cmTryRunResult const& runResult) |
42 | 0 | { |
43 | | // Keep in sync with cmFileAPIConfigureLog's DumpEventKindNames. |
44 | 0 | static std::vector<unsigned int> const LogVersionsWithTryRunV1{ 1 }; |
45 | |
|
46 | 0 | if (log.IsAnyLogVersionEnabled(LogVersionsWithTryRunV1)) { |
47 | 0 | log.BeginEvent("try_run-v1", mf); |
48 | 0 | cmCoreTryCompile::WriteTryCompileEventFields(log, compileResult); |
49 | |
|
50 | 0 | log.BeginObject("runResult"_s); |
51 | 0 | log.WriteValue("variable"_s, runResult.Variable); |
52 | 0 | log.WriteValue("cached"_s, runResult.VariableCached); |
53 | 0 | if (runResult.Stdout) { |
54 | 0 | log.WriteLiteralTextBlock("stdout"_s, *runResult.Stdout); |
55 | 0 | } |
56 | 0 | if (runResult.Stderr) { |
57 | 0 | log.WriteLiteralTextBlock("stderr"_s, *runResult.Stderr); |
58 | 0 | } |
59 | 0 | if (runResult.ExitCode) { |
60 | 0 | try { |
61 | 0 | log.WriteValue("exitCode"_s, std::stoi(*runResult.ExitCode)); |
62 | 0 | } catch (std::invalid_argument const&) { |
63 | 0 | log.WriteValue("exitCode"_s, *runResult.ExitCode); |
64 | 0 | } |
65 | 0 | } |
66 | 0 | log.EndObject(); |
67 | 0 | log.EndEvent(); |
68 | 0 | } |
69 | 0 | } |
70 | | #endif |
71 | | |
72 | | class TryRunCommandImpl : public cmCoreTryCompile |
73 | | { |
74 | | public: |
75 | | TryRunCommandImpl(cmMakefile* mf) |
76 | 0 | : cmCoreTryCompile(mf) |
77 | 0 | { |
78 | 0 | } |
79 | | |
80 | | bool TryRunCode(std::vector<std::string> const& args); |
81 | | |
82 | | void RunExecutable(std::string const& runArgs, |
83 | | cm::optional<std::string> const& workDir, |
84 | | std::string* runOutputContents, |
85 | | std::string* runOutputStdOutContents, |
86 | | std::string* runOutputStdErrContents); |
87 | | void DoNotRunExecutable(std::string const& runArgs, |
88 | | cm::optional<std::string> const& srcFile, |
89 | | std::string const& compileResultVariable, |
90 | | std::string* runOutputContents, |
91 | | std::string* runOutputStdOutContents, |
92 | | std::string* runOutputStdErrContents, |
93 | | bool stdOutErrRequired); |
94 | | |
95 | | bool NoCache; |
96 | | std::string RunResultVariable; |
97 | | }; |
98 | | |
99 | | bool TryRunCommandImpl::TryRunCode(std::vector<std::string> const& argv) |
100 | 0 | { |
101 | 0 | this->RunResultVariable = argv[0]; |
102 | 0 | cmCoreTryCompile::Arguments arguments = |
103 | 0 | this->ParseArgs(cmMakeRange(argv).advance(1), true); |
104 | 0 | if (!arguments) { |
105 | 0 | return true; |
106 | 0 | } |
107 | 0 | this->NoCache = arguments.NoCache; |
108 | | |
109 | | // although they could be used together, don't allow it, because |
110 | | // using OUTPUT_VARIABLE makes crosscompiling harder |
111 | 0 | if (arguments.OutputVariable && |
112 | 0 | (arguments.CompileOutputVariable || arguments.RunOutputVariable || |
113 | 0 | arguments.RunOutputStdOutVariable || |
114 | 0 | arguments.RunOutputStdErrVariable)) { |
115 | 0 | cmSystemTools::Error( |
116 | 0 | "You cannot use OUTPUT_VARIABLE together with COMPILE_OUTPUT_VARIABLE " |
117 | 0 | ", RUN_OUTPUT_VARIABLE, RUN_OUTPUT_STDOUT_VARIABLE or " |
118 | 0 | "RUN_OUTPUT_STDERR_VARIABLE. " |
119 | 0 | "Please use only COMPILE_OUTPUT_VARIABLE, RUN_OUTPUT_VARIABLE, " |
120 | 0 | "RUN_OUTPUT_STDOUT_VARIABLE " |
121 | 0 | "and/or RUN_OUTPUT_STDERR_VARIABLE."); |
122 | 0 | return false; |
123 | 0 | } |
124 | | |
125 | 0 | if ((arguments.RunOutputStdOutVariable || |
126 | 0 | arguments.RunOutputStdErrVariable) && |
127 | 0 | arguments.RunOutputVariable) { |
128 | 0 | cmSystemTools::Error( |
129 | 0 | "You cannot use RUN_OUTPUT_STDOUT_VARIABLE or " |
130 | 0 | "RUN_OUTPUT_STDERR_VARIABLE together " |
131 | 0 | "with RUN_OUTPUT_VARIABLE. Please use only COMPILE_OUTPUT_VARIABLE or " |
132 | 0 | "RUN_OUTPUT_STDOUT_VARIABLE and/or RUN_OUTPUT_STDERR_VARIABLE."); |
133 | 0 | return false; |
134 | 0 | } |
135 | | |
136 | 0 | if (arguments.RunWorkingDirectory) { |
137 | 0 | if (!cmSystemTools::MakeDirectory(*arguments.RunWorkingDirectory)) { |
138 | 0 | cmSystemTools::Error(cmStrCat("Error creating working directory \"", |
139 | 0 | *arguments.RunWorkingDirectory, "\".")); |
140 | 0 | return false; |
141 | 0 | } |
142 | 0 | } |
143 | | |
144 | 0 | bool captureRunOutput = false; |
145 | 0 | if (arguments.OutputVariable) { |
146 | 0 | captureRunOutput = true; |
147 | 0 | } else if (arguments.CompileOutputVariable) { |
148 | 0 | arguments.OutputVariable = arguments.CompileOutputVariable; |
149 | 0 | } |
150 | | |
151 | | // Capture the split output for the configure log unless the caller |
152 | | // requests combined output to be captured by a variable. |
153 | 0 | bool captureRunOutputStdOutErr = true; |
154 | 0 | if (!arguments.RunOutputStdOutVariable && |
155 | 0 | !arguments.RunOutputStdErrVariable) { |
156 | 0 | if (arguments.RunOutputVariable) { |
157 | 0 | captureRunOutput = true; |
158 | 0 | captureRunOutputStdOutErr = false; |
159 | 0 | } else if (arguments.OutputVariable) { |
160 | 0 | captureRunOutputStdOutErr = false; |
161 | 0 | } |
162 | 0 | } |
163 | | |
164 | | // do the try compile |
165 | 0 | cm::optional<cmTryCompileResult> compileResult = |
166 | 0 | this->TryCompileCode(arguments, cmStateEnums::EXECUTABLE); |
167 | |
|
168 | 0 | cmTryRunResult runResult; |
169 | 0 | runResult.Variable = this->RunResultVariable; |
170 | 0 | runResult.VariableCached = !arguments.NoCache; |
171 | | |
172 | | // now try running the command if it compiled |
173 | 0 | if (compileResult && compileResult->ExitCode == 0) { |
174 | 0 | if (this->OutputFile.empty()) { |
175 | 0 | cmSystemTools::Error(this->FindErrorMessage); |
176 | 0 | } else { |
177 | 0 | std::string runArgs; |
178 | 0 | if (arguments.RunArgs) { |
179 | 0 | runArgs = cmStrCat(' ', cmJoin(*arguments.RunArgs, " ")); |
180 | 0 | } |
181 | | |
182 | | // "run" it and capture the output |
183 | 0 | std::string runOutputContents; |
184 | 0 | std::string runOutputStdOutContents; |
185 | 0 | std::string runOutputStdErrContents; |
186 | 0 | if (this->Makefile->IsOn("CMAKE_CROSSCOMPILING") && |
187 | 0 | !this->Makefile->IsDefinitionSet("CMAKE_CROSSCOMPILING_EMULATOR")) { |
188 | | // We only require the stdout/stderr cache entries if the project |
189 | | // actually asked for the values, not just for logging. |
190 | 0 | bool const stdOutErrRequired = (arguments.RunOutputStdOutVariable || |
191 | 0 | arguments.RunOutputStdErrVariable); |
192 | 0 | this->DoNotRunExecutable( |
193 | 0 | runArgs, arguments.SourceDirectoryOrFile, |
194 | 0 | *arguments.CompileResultVariable, |
195 | 0 | captureRunOutput ? &runOutputContents : nullptr, |
196 | 0 | captureRunOutputStdOutErr ? &runOutputStdOutContents : nullptr, |
197 | 0 | captureRunOutputStdOutErr ? &runOutputStdErrContents : nullptr, |
198 | 0 | stdOutErrRequired); |
199 | 0 | } else { |
200 | 0 | this->RunExecutable( |
201 | 0 | runArgs, arguments.RunWorkingDirectory, |
202 | 0 | captureRunOutput ? &runOutputContents : nullptr, |
203 | 0 | captureRunOutputStdOutErr ? &runOutputStdOutContents : nullptr, |
204 | 0 | captureRunOutputStdOutErr ? &runOutputStdErrContents : nullptr); |
205 | 0 | } |
206 | |
|
207 | 0 | if (captureRunOutputStdOutErr) { |
208 | 0 | runResult.Stdout = runOutputStdOutContents; |
209 | 0 | runResult.Stderr = runOutputStdErrContents; |
210 | 0 | } else { |
211 | 0 | runResult.Stdout = runOutputContents; |
212 | 0 | } |
213 | |
|
214 | 0 | if (cmValue ec = |
215 | 0 | this->Makefile->GetDefinition(this->RunResultVariable)) { |
216 | 0 | runResult.ExitCode = *ec; |
217 | 0 | } |
218 | | |
219 | | // now put the output into the variables |
220 | 0 | if (arguments.RunOutputVariable) { |
221 | 0 | this->Makefile->AddDefinition(*arguments.RunOutputVariable, |
222 | 0 | runOutputContents); |
223 | 0 | } |
224 | 0 | if (arguments.RunOutputStdOutVariable) { |
225 | 0 | this->Makefile->AddDefinition(*arguments.RunOutputStdOutVariable, |
226 | 0 | runOutputStdOutContents); |
227 | 0 | } |
228 | 0 | if (arguments.RunOutputStdErrVariable) { |
229 | 0 | this->Makefile->AddDefinition(*arguments.RunOutputStdErrVariable, |
230 | 0 | runOutputStdErrContents); |
231 | 0 | } |
232 | |
|
233 | 0 | if (arguments.OutputVariable && !arguments.CompileOutputVariable) { |
234 | | // if the TryCompileCore saved output in this outputVariable then |
235 | | // prepend that output to this output |
236 | 0 | cmValue compileOutput = |
237 | 0 | this->Makefile->GetDefinition(*arguments.OutputVariable); |
238 | 0 | if (compileOutput) { |
239 | 0 | runOutputContents = *compileOutput + runOutputContents; |
240 | 0 | } |
241 | 0 | this->Makefile->AddDefinition(*arguments.OutputVariable, |
242 | 0 | runOutputContents); |
243 | 0 | } |
244 | 0 | } |
245 | 0 | } |
246 | |
|
247 | 0 | #ifndef CMAKE_BOOTSTRAP |
248 | 0 | if (compileResult && !arguments.NoLog) { |
249 | 0 | cmMakefile const& mf = *(this->Makefile); |
250 | 0 | if (cmConfigureLog* log = mf.GetCMakeInstance()->GetConfigureLog()) { |
251 | 0 | WriteTryRunEvent(*log, mf, *compileResult, runResult); |
252 | 0 | } |
253 | 0 | } |
254 | 0 | #endif |
255 | | |
256 | | // if we created a directory etc, then cleanup after ourselves |
257 | 0 | if (!this->Makefile->GetCMakeInstance()->GetDebugTryCompile()) { |
258 | 0 | this->CleanupFiles(this->BinaryDirectory); |
259 | 0 | } |
260 | 0 | return true; |
261 | 0 | } |
262 | | |
263 | | void TryRunCommandImpl::RunExecutable(std::string const& runArgs, |
264 | | cm::optional<std::string> const& workDir, |
265 | | std::string* out, std::string* stdOut, |
266 | | std::string* stdErr) |
267 | 0 | { |
268 | 0 | int retVal = -1; |
269 | |
|
270 | 0 | std::string finalCommand; |
271 | 0 | std::string const& emulator = |
272 | 0 | this->Makefile->GetSafeDefinition("CMAKE_CROSSCOMPILING_EMULATOR"); |
273 | 0 | if (!emulator.empty()) { |
274 | 0 | cmList emulatorWithArgs{ emulator }; |
275 | 0 | finalCommand += cmStrCat( |
276 | 0 | cmSystemTools::ConvertToRunCommandPath(emulatorWithArgs[0]), ' ', |
277 | 0 | cmWrap("\"", cmMakeRange(emulatorWithArgs).advance(1), "\"", " "), ' '); |
278 | 0 | } |
279 | 0 | finalCommand += cmSystemTools::ConvertToRunCommandPath(this->OutputFile); |
280 | 0 | if (!runArgs.empty()) { |
281 | 0 | finalCommand += runArgs; |
282 | 0 | } |
283 | 0 | bool worked = cmSystemTools::RunSingleCommand( |
284 | 0 | finalCommand, stdOut || stdErr ? stdOut : out, |
285 | 0 | stdOut || stdErr ? stdErr : out, &retVal, |
286 | 0 | workDir ? workDir->c_str() : nullptr, cmSystemTools::OUTPUT_NONE, |
287 | 0 | cmDuration::zero()); |
288 | | // set the run var |
289 | 0 | std::string retStr = worked ? std::to_string(retVal) : "FAILED_TO_RUN"; |
290 | 0 | if (this->NoCache) { |
291 | 0 | this->Makefile->AddDefinition(this->RunResultVariable, retStr); |
292 | 0 | } else { |
293 | 0 | this->Makefile->AddCacheDefinition(this->RunResultVariable, retStr, |
294 | 0 | "Result of try_run()", |
295 | 0 | cmStateEnums::INTERNAL); |
296 | 0 | } |
297 | 0 | } |
298 | | |
299 | | /* This is only used when cross compiling. Instead of running the |
300 | | executable, two cache variables are created which will hold the results |
301 | | the executable would have produced. |
302 | | */ |
303 | | void TryRunCommandImpl::DoNotRunExecutable( |
304 | | std::string const& runArgs, cm::optional<std::string> const& srcFile, |
305 | | std::string const& compileResultVariable, std::string* out, |
306 | | std::string* stdOut, std::string* stdErr, bool stdOutErrRequired) |
307 | 0 | { |
308 | | // copy the executable out of the CMakeFiles/ directory, so it is not |
309 | | // removed at the end of try_run() and the user can run it manually |
310 | | // on the target platform. |
311 | 0 | std::string copyDest = |
312 | 0 | cmStrCat(this->Makefile->GetHomeOutputDirectory(), "/CMakeFiles/", |
313 | 0 | cmSystemTools::GetFilenameWithoutExtension(this->OutputFile), '-', |
314 | 0 | this->RunResultVariable, |
315 | 0 | cmSystemTools::GetFilenameExtension(this->OutputFile)); |
316 | 0 | cmSystemTools::CopyFileAlways(this->OutputFile, copyDest); |
317 | |
|
318 | 0 | std::string resultFileName = |
319 | 0 | cmStrCat(this->Makefile->GetHomeOutputDirectory(), "/TryRunResults.cmake"); |
320 | |
|
321 | 0 | std::string detailsString = cmStrCat("For details see ", resultFileName); |
322 | |
|
323 | 0 | std::string internalRunOutputName = |
324 | 0 | this->RunResultVariable + "__TRYRUN_OUTPUT"; |
325 | 0 | std::string internalRunOutputStdOutName = |
326 | 0 | this->RunResultVariable + "__TRYRUN_OUTPUT_STDOUT"; |
327 | 0 | std::string internalRunOutputStdErrName = |
328 | 0 | this->RunResultVariable + "__TRYRUN_OUTPUT_STDERR"; |
329 | 0 | bool error = false; |
330 | |
|
331 | 0 | if (!this->Makefile->GetDefinition(this->RunResultVariable)) { |
332 | | // if the variables doesn't exist, create it with a helpful error text |
333 | | // and mark it as advanced |
334 | 0 | std::string comment = |
335 | 0 | cmStrCat("Run result of try_run(), indicates whether the executable " |
336 | 0 | "would have been able to run on its target platform.\n", |
337 | 0 | detailsString); |
338 | 0 | this->Makefile->AddCacheDefinition(this->RunResultVariable, |
339 | 0 | "PLEASE_FILL_OUT-FAILED_TO_RUN", |
340 | 0 | comment, cmStateEnums::STRING); |
341 | |
|
342 | 0 | cmState* state = this->Makefile->GetState(); |
343 | 0 | cmValue existingValue = state->GetCacheEntryValue(this->RunResultVariable); |
344 | 0 | if (existingValue) { |
345 | 0 | state->SetCacheEntryProperty(this->RunResultVariable, "ADVANCED", "1"); |
346 | 0 | } |
347 | |
|
348 | 0 | error = true; |
349 | 0 | } |
350 | | |
351 | | // is the output from the executable used ? |
352 | 0 | if (stdOutErrRequired) { |
353 | 0 | if (!this->Makefile->GetDefinition(internalRunOutputStdOutName)) { |
354 | | // if the variables doesn't exist, create it with a helpful error text |
355 | | // and mark it as advanced |
356 | 0 | std::string comment = cmStrCat( |
357 | 0 | "Output of try_run(), contains the text, which the executable " |
358 | 0 | "would have printed on stdout on its target platform.\n", |
359 | 0 | detailsString); |
360 | |
|
361 | 0 | this->Makefile->AddCacheDefinition(internalRunOutputStdOutName, |
362 | 0 | "PLEASE_FILL_OUT-NOTFOUND", comment, |
363 | 0 | cmStateEnums::STRING); |
364 | 0 | cmState* state = this->Makefile->GetState(); |
365 | 0 | cmValue existing = |
366 | 0 | state->GetCacheEntryValue(internalRunOutputStdOutName); |
367 | 0 | if (existing) { |
368 | 0 | state->SetCacheEntryProperty(internalRunOutputStdOutName, "ADVANCED", |
369 | 0 | "1"); |
370 | 0 | } |
371 | |
|
372 | 0 | error = true; |
373 | 0 | } |
374 | |
|
375 | 0 | if (!this->Makefile->GetDefinition(internalRunOutputStdErrName)) { |
376 | | // if the variables doesn't exist, create it with a helpful error text |
377 | | // and mark it as advanced |
378 | 0 | std::string comment = cmStrCat( |
379 | 0 | "Output of try_run(), contains the text, which the executable " |
380 | 0 | "would have printed on stderr on its target platform.\n", |
381 | 0 | detailsString); |
382 | |
|
383 | 0 | this->Makefile->AddCacheDefinition(internalRunOutputStdErrName, |
384 | 0 | "PLEASE_FILL_OUT-NOTFOUND", comment, |
385 | 0 | cmStateEnums::STRING); |
386 | 0 | cmState* state = this->Makefile->GetState(); |
387 | 0 | cmValue existing = |
388 | 0 | state->GetCacheEntryValue(internalRunOutputStdErrName); |
389 | 0 | if (existing) { |
390 | 0 | state->SetCacheEntryProperty(internalRunOutputStdErrName, "ADVANCED", |
391 | 0 | "1"); |
392 | 0 | } |
393 | |
|
394 | 0 | error = true; |
395 | 0 | } |
396 | 0 | } else if (out) { |
397 | 0 | if (!this->Makefile->GetDefinition(internalRunOutputName)) { |
398 | | // if the variables doesn't exist, create it with a helpful error text |
399 | | // and mark it as advanced |
400 | 0 | std::string comment = cmStrCat( |
401 | 0 | "Output of try_run(), contains the text, which the executable " |
402 | 0 | "would have printed on stdout and stderr on its target platform.\n", |
403 | 0 | detailsString); |
404 | |
|
405 | 0 | this->Makefile->AddCacheDefinition(internalRunOutputName, |
406 | 0 | "PLEASE_FILL_OUT-NOTFOUND", comment, |
407 | 0 | cmStateEnums::STRING); |
408 | 0 | cmState* state = this->Makefile->GetState(); |
409 | 0 | cmValue existing = state->GetCacheEntryValue(internalRunOutputName); |
410 | 0 | if (existing) { |
411 | 0 | state->SetCacheEntryProperty(internalRunOutputName, "ADVANCED", "1"); |
412 | 0 | } |
413 | |
|
414 | 0 | error = true; |
415 | 0 | } |
416 | 0 | } |
417 | |
|
418 | 0 | if (error) { |
419 | 0 | static bool firstTryRun = true; |
420 | 0 | cmsys::ofstream file(resultFileName.c_str(), |
421 | 0 | firstTryRun ? std::ios::out : std::ios::app); |
422 | 0 | if (file) { |
423 | 0 | if (firstTryRun) { |
424 | | /* clang-format off */ |
425 | 0 | file << "# This file was generated by CMake because it detected " |
426 | 0 | "try_run() commands\n" |
427 | 0 | "# in crosscompiling mode. It will be overwritten by the next " |
428 | 0 | "CMake run.\n" |
429 | 0 | "# Copy it to a safe location, set the variables to " |
430 | 0 | "appropriate values\n" |
431 | 0 | "# and use it then to preset the CMake cache (using -C).\n\n"; |
432 | | /* clang-format on */ |
433 | 0 | } |
434 | |
|
435 | 0 | std::string comment = |
436 | 0 | cmStrCat('\n', this->RunResultVariable, |
437 | 0 | "\n indicates whether the executable would have been able " |
438 | 0 | "to run on its\n" |
439 | 0 | " target platform. If so, set ", |
440 | 0 | this->RunResultVariable, |
441 | 0 | " to\n" |
442 | 0 | " the exit code (in many cases 0 for success), otherwise " |
443 | 0 | "enter \"FAILED_TO_RUN\".\n"); |
444 | 0 | if (stdOut || stdErr) { |
445 | 0 | if (stdOut) { |
446 | 0 | comment += cmStrCat( |
447 | 0 | internalRunOutputStdOutName, |
448 | 0 | "\n contains the text the executable would have printed on " |
449 | 0 | "stdout.\n" |
450 | 0 | " If the executable would not have been able to run, set ", |
451 | 0 | internalRunOutputStdOutName, |
452 | 0 | " empty.\n" |
453 | 0 | " Otherwise check if the output is evaluated by the " |
454 | 0 | "calling CMake code. If so,\n" |
455 | 0 | " check what the source file would have printed when " |
456 | 0 | "called with the given arguments.\n"); |
457 | 0 | } |
458 | 0 | if (stdErr) { |
459 | 0 | comment += cmStrCat( |
460 | 0 | internalRunOutputStdErrName, |
461 | 0 | "\n contains the text the executable would have printed on " |
462 | 0 | "stderr.\n" |
463 | 0 | " If the executable would not have been able to run, set ", |
464 | 0 | internalRunOutputStdErrName, |
465 | 0 | " empty.\n" |
466 | 0 | " Otherwise check if the output is evaluated by the " |
467 | 0 | "calling CMake code. If so,\n" |
468 | 0 | " check what the source file would have printed when " |
469 | 0 | "called with the given arguments.\n"); |
470 | 0 | } |
471 | 0 | } else if (out) { |
472 | 0 | comment += cmStrCat( |
473 | 0 | internalRunOutputName, |
474 | 0 | "\n contains the text the executable would have printed on stdout " |
475 | 0 | "and stderr.\n" |
476 | 0 | " If the executable would not have been able to run, set ", |
477 | 0 | internalRunOutputName, |
478 | 0 | " empty.\n" |
479 | 0 | " Otherwise check if the output is evaluated by the " |
480 | 0 | "calling CMake code. If so,\n" |
481 | 0 | " check what the source file would have printed when " |
482 | 0 | "called with the given arguments.\n"); |
483 | 0 | } |
484 | |
|
485 | 0 | comment += |
486 | 0 | cmStrCat("The ", compileResultVariable, |
487 | 0 | " variable holds the build result for this try_run().\n\n"); |
488 | 0 | if (srcFile) { |
489 | 0 | comment += cmStrCat("Source file : ", *srcFile, '\n'); |
490 | 0 | } |
491 | 0 | comment += cmStrCat("Executable : ", copyDest, |
492 | 0 | "\n" |
493 | 0 | "Run arguments : ", |
494 | 0 | runArgs, |
495 | 0 | "\n" |
496 | 0 | " Called from: ", |
497 | 0 | this->Makefile->FormatListFileStack()); |
498 | 0 | cmsys::SystemTools::ReplaceString(comment, "\n", "\n# "); |
499 | 0 | file << comment << "\n\n"; |
500 | |
|
501 | 0 | file << "set( " << this->RunResultVariable << " \n \"" |
502 | 0 | << this->Makefile->GetSafeDefinition(this->RunResultVariable) |
503 | 0 | << "\"\n CACHE STRING \"Result from try_run\" FORCE)\n\n"; |
504 | |
|
505 | 0 | if (out) { |
506 | 0 | file << "set( " << internalRunOutputName << " \n \"" |
507 | 0 | << this->Makefile->GetSafeDefinition(internalRunOutputName) |
508 | 0 | << "\"\n CACHE STRING \"Output from try_run\" FORCE)\n\n"; |
509 | 0 | } |
510 | 0 | file.close(); |
511 | 0 | } |
512 | 0 | firstTryRun = false; |
513 | |
|
514 | 0 | std::string errorMessage = |
515 | 0 | cmStrCat("try_run() invoked in cross-compiling mode, " |
516 | 0 | "please set the following cache variables " |
517 | 0 | "appropriately:\n ", |
518 | 0 | this->RunResultVariable, " (advanced)\n"); |
519 | 0 | if (out) { |
520 | 0 | errorMessage += " " + internalRunOutputName + " (advanced)\n"; |
521 | 0 | } |
522 | 0 | errorMessage += detailsString; |
523 | 0 | cmSystemTools::Error(errorMessage); |
524 | 0 | return; |
525 | 0 | } |
526 | | |
527 | 0 | if (stdOut || stdErr) { |
528 | 0 | if (stdOut) { |
529 | 0 | (*stdOut) = *this->Makefile->GetDefinition(internalRunOutputStdOutName); |
530 | 0 | } |
531 | 0 | if (stdErr) { |
532 | 0 | (*stdErr) = *this->Makefile->GetDefinition(internalRunOutputStdErrName); |
533 | 0 | } |
534 | 0 | } else if (out) { |
535 | 0 | (*out) = *this->Makefile->GetDefinition(internalRunOutputName); |
536 | 0 | } |
537 | 0 | } |
538 | | } |
539 | | |
540 | | bool cmTryRunCommand(std::vector<std::string> const& args, |
541 | | cmExecutionStatus& status) |
542 | 0 | { |
543 | 0 | cmMakefile& mf = status.GetMakefile(); |
544 | |
|
545 | 0 | if (args.size() < 4) { |
546 | 0 | mf.IssueMessage(MessageType::FATAL_ERROR, |
547 | 0 | "The try_run() command requires at least 4 arguments."); |
548 | 0 | return false; |
549 | 0 | } |
550 | | |
551 | 0 | if (mf.GetCMakeInstance()->GetState()->GetRole() == |
552 | 0 | cmState::Role::FindPackage) { |
553 | 0 | mf.IssueMessage( |
554 | 0 | MessageType::FATAL_ERROR, |
555 | 0 | "The try_run() command is not supported in --find-package mode."); |
556 | 0 | return false; |
557 | 0 | } |
558 | | |
559 | 0 | TryRunCommandImpl tr(&mf); |
560 | 0 | return tr.TryRunCode(args); |
561 | 0 | } |