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