* @throws InternalError thrown when execution fails */
| 433 | * @throws InternalError thrown when execution fails |
| 434 | */ |
| 435 | static std::vector<picojson::value> executeAddon(const AddonInfo &addonInfo, |
| 436 | const std::string &defaultPythonExe, |
| 437 | const std::string &file, |
| 438 | const std::string &premiumArgs, |
| 439 | const CppCheck::ExecuteCmdFn &executeCommand) |
| 440 | { |
| 441 | if (!executeCommand) |
| 442 | throw InternalError(nullptr, "Failed to execute addon - no command callback provided"); |
| 443 | |
| 444 | std::string pythonExe; |
| 445 | |
| 446 | if (!addonInfo.executable.empty()) |
| 447 | pythonExe = addonInfo.executable; |
| 448 | else if (!addonInfo.python.empty()) |
| 449 | pythonExe = cmdFileName(addonInfo.python); |
| 450 | else if (!defaultPythonExe.empty()) |
| 451 | pythonExe = cmdFileName(defaultPythonExe); |
| 452 | else { |
| 453 | // store in static variable so we only look this up once - TODO: do not cache globally |
| 454 | static const std::string detectedPythonExe = detectPython(executeCommand); |
| 455 | if (detectedPythonExe.empty()) |
| 456 | throw InternalError(nullptr, "Failed to auto detect python"); |
| 457 | pythonExe = detectedPythonExe; |
| 458 | } |
| 459 | |
| 460 | std::string args; |
| 461 | if (addonInfo.executable.empty()) |
| 462 | args = cmdFileName(addonInfo.runScript) + " " + cmdFileName(addonInfo.scriptFile); |
| 463 | args += std::string(args.empty() ? "" : " ") + "--cli" + addonInfo.args; |
| 464 | if (!premiumArgs.empty() && !addonInfo.executable.empty()) |
| 465 | args += " " + premiumArgs; |
| 466 | |
| 467 | const bool is_file_list = (file.find(FILELIST) != std::string::npos); |
| 468 | const std::string fileArg = (is_file_list ? " --file-list " : " ") + cmdFileName(file); |
| 469 | args += fileArg; |
| 470 | |
| 471 | std::string result; |
| 472 | if (const int exitcode = executeCommand(pythonExe, split(args), "2>&1", result)) { |
| 473 | std::string message("Failed to execute addon '" + addonInfo.name + "' - exitcode is " + std::to_string(exitcode)); |
| 474 | std::string details = pythonExe + " " + args; |
| 475 | if (result.size() > 2) { |
| 476 | details += "\nOutput:\n"; |
| 477 | details += result; |
| 478 | const auto pos = details.find_last_not_of("\n\r"); |
| 479 | if (pos != std::string::npos) |
| 480 | details.resize(pos + 1); |
| 481 | } |
| 482 | throw InternalError(nullptr, std::move(message), std::move(details)); |
| 483 | } |
| 484 | |
| 485 | std::vector<picojson::value> addonResult; |
| 486 | |
| 487 | // Validate output.. |
| 488 | std::istringstream istr(result); |
| 489 | std::string line; |
| 490 | while (std::getline(istr, line)) { |
| 491 | // TODO: also bail out? |
| 492 | if (line.empty()) { |
no test coverage detected