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