coverage-helper.cpp 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. #include <algorithm>
  2. #include <array>
  3. #include <cassert>
  4. #include <fstream>
  5. #include <iostream>
  6. #include <memory>
  7. #include <numeric>
  8. #include <regex>
  9. #include <string>
  10. #include <vector>
  11. std::string escape_arg(const std::string& arg) {
  12. if (arg.empty() == false &&
  13. arg.find_first_of(" \t\n\v\"") == arg.npos) {
  14. return arg;
  15. }
  16. std::string escaped;
  17. escaped.push_back('"');
  18. for (auto it = arg.begin(); ; ++it) {
  19. int num_backslashes = 0;
  20. while (it != arg.end() && *it == '\\') {
  21. ++it;
  22. ++num_backslashes;
  23. }
  24. if (it == arg.end()) {
  25. escaped.append(num_backslashes * 2, '\\');
  26. break;
  27. } else if (*it == '"') {
  28. escaped.append((num_backslashes + 1) * 2, '\\');
  29. escaped.push_back('"');
  30. escaped.push_back(*it);
  31. } else {
  32. escaped.append(num_backslashes, '\\');
  33. escaped.push_back(*it);
  34. }
  35. }
  36. escaped.push_back('"');
  37. return escaped;
  38. }
  39. void create_empty_file(std::string const& path) {
  40. std::ofstream ofs(path);
  41. ofs << '\n';
  42. }
  43. const std::string separator = "--sep--";
  44. const std::string logfile_prefix = "--log-file=";
  45. bool starts_with(std::string const& str, std::string const& pref) {
  46. return str.find(pref) == 0;
  47. }
  48. int parse_log_file_arg(std::string const& arg) {
  49. assert(starts_with(arg, logfile_prefix) && "Attempting to parse incorrect arg!");
  50. auto fname = arg.substr(logfile_prefix.size());
  51. create_empty_file(fname);
  52. std::regex regex("MemoryChecker\\.(\\d+)\\.log", std::regex::icase);
  53. std::smatch match;
  54. if (std::regex_search(fname, match, regex)) {
  55. return std::stoi(match[1]);
  56. } else {
  57. throw std::domain_error("Couldn't find desired expression in string: " + fname);
  58. }
  59. }
  60. std::string catch_path(std::string path) {
  61. auto start = path.find("catch");
  62. // try capitalized instead
  63. if (start == std::string::npos) {
  64. start = path.find("Catch");
  65. }
  66. if (start == std::string::npos) {
  67. throw std::domain_error("Couldn't find Catch's base path");
  68. }
  69. auto end = path.find_first_of("\\/", start);
  70. return path.substr(0, end);
  71. }
  72. std::string windowsify_path(std::string path) {
  73. for (auto& c : path) {
  74. if (c == '/') {
  75. c = '\\';
  76. }
  77. }
  78. return path;
  79. }
  80. int exec_cmd(std::string const& cmd, int log_num, std::string const& path) {
  81. std::array<char, 128> buffer;
  82. // cmd has already been escaped outside this function.
  83. auto real_cmd = "OpenCppCoverage --export_type binary:cov-report" + std::to_string(log_num)
  84. + ".bin --quiet " + "--sources " + escape_arg(path) + " --cover_children -- " + cmd;
  85. std::cout << "=== Marker ===: Cmd: " << real_cmd << '\n';
  86. auto pipe = _popen(real_cmd.c_str(), "r");
  87. if (!pipe) {
  88. throw std::runtime_error("popen() failed!");
  89. }
  90. while (!feof(pipe)) {
  91. if (fgets(buffer.data(), 128, pipe) != nullptr) {
  92. std::cout << buffer.data();
  93. }
  94. }
  95. auto ret = _pclose(pipe);
  96. if (ret == -1) {
  97. throw std::runtime_error("underlying error in pclose()");
  98. }
  99. return ret;
  100. }
  101. // argv should be:
  102. // [0]: our path
  103. // [1]: "--log-file=<path>"
  104. // [2]: "--sep--"
  105. // [3]+: the actual command
  106. int main(int argc, char** argv) {
  107. std::vector<std::string> args(argv, argv + argc);
  108. auto sep = std::find(begin(args), end(args), separator);
  109. assert(sep - begin(args) == 2 && "Structure differs from expected!");
  110. auto num = parse_log_file_arg(args[1]);
  111. auto cmdline = std::accumulate(++sep, end(args), std::string{}, [] (const std::string& lhs, const std::string& rhs) {
  112. return lhs + ' ' + escape_arg(rhs);
  113. });
  114. try {
  115. return exec_cmd(cmdline, num, windowsify_path(catch_path(args[0])));
  116. } catch (std::exception const& ex) {
  117. std::cerr << "Helper failed with: '" << ex.what() << "'\n";
  118. return 12;
  119. }
  120. }