• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
escape_arg(const std::string & arg)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 
create_empty_file(std::string const & path)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 
starts_with(std::string const & str,std::string const & pref)53 bool starts_with(std::string const& str, std::string const& pref) {
54     return str.find(pref) == 0;
55 }
56 
parse_log_file_arg(std::string const & arg)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 
catch_path(std::string path)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 
windowsify_path(std::string path)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 
exec_cmd(std::string const & cmd,int log_num,std::string const & path)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 
main(int argc,char ** argv)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