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