Merge pull request #29 from BotondBaranyi/master
[deliverable/titan.core.git] / compiler2 / tcov2lcov.cc
1 /******************************************************************************
2 * Copyright (c) 2000-2016 Ericsson Telecom AB
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
7 *
8 * Contributors:
9 * Balasko, Jeno
10 * Kovacs, Ferenc
11 * Lovassy, Arpad
12 * Raduly, Csaba
13 *
14 ******************************************************************************/
15 #include "tcov2lcov.hh"
16
17 #include <cstdlib>
18 #include <iostream>
19 #include <fstream>
20
21 #include <unistd.h>
22 #include <string.h>
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <dirent.h>
26
27 #include <libxml/xmlschemastypes.h>
28 #include <libxml/xmlreader.h>
29
30 #include "../common/version_internal.h"
31 #include "../common/license.h"
32
33 // The new (T)itan(C)overage(D)ata is born!
34 const std::string tcd_ext(".tcd");
35 size_t tcd_ext_len = tcd_ext.size();
36
37 #ifdef WIN32
38 #define P_SEP "\\"
39 #else
40 #define P_SEP "/"
41 #endif
42
43 void TcovData::inc_function(const std::string& function, int n)
44 {
45 if (m_functions.count(function) == 0) m_functions[function] = 0;
46 m_functions[function] += n;
47 }
48
49 void TcovData::inc_line(int line, int n)
50 {
51 if (m_lines.count(line) == 0) m_lines[line] = 0;
52 m_lines[line] += n;
53 }
54
55 Tcov2Lcov::Tcov2Lcov(const char *code_base, const char *input_dir, const char *output_file, const char *xsd_file)
56 : m_code_base(code_base), m_input_dir(input_dir), m_output_file(output_file), m_xsd_file(xsd_file)
57 {
58 m_ver_major = m_ver_minor = -1;
59 }
60
61 Tcov2Lcov::~Tcov2Lcov()
62 {
63 std::map<std::string, TcovData *>::iterator i = m_data.begin();
64 for (; i != m_data.end(); ++i) {
65 delete (*i).second;
66 }
67 m_data.clear();
68 }
69
70 int Tcov2Lcov::collect_dir(std::string dir)
71 {
72 DIR *d = NULL;
73 if ((d = opendir(dir.c_str())) == NULL) {
74 // Fatal for top-level directory.
75 std::cerr << "Error while opening directory `" << dir << "'" << std::endl;
76 return -1;
77 }
78 struct dirent *de = NULL;
79 while ((de = readdir(d)) != NULL) {
80 std::string file_name(de->d_name);
81 std::string full_file_name = dir + P_SEP + file_name;
82 // No `d_type' field on Solaris...
83 struct stat st;
84 int stat_ret_val = stat(de->d_name, &st);
85 if (stat_ret_val != 0)
86 continue;
87 if (S_ISDIR(st.st_mode)) {
88 if (file_name == ".." || file_name == ".")
89 continue;
90 collect_dir(full_file_name);
91 } else {
92 size_t file_name_len = file_name.size();
93 if (file_name_len > tcd_ext_len
94 && file_name.substr(file_name_len - tcd_ext_len) == tcd_ext) {
95 m_files.push_back(full_file_name);
96 }
97 }
98 }
99 closedir(d);
100 return 0;
101 }
102
103 int Tcov2Lcov::collect()
104 {
105 if (collect_dir(m_input_dir) < 0)
106 return -1;
107 return static_cast<int>(m_files.size());
108 }
109
110 void Tcov2Lcov::d_print_files() const
111 {
112 std::cout << "Collected XML/TCD files:" << std::endl;
113 for (size_t i = 0; i < m_files.size(); ++i)
114 std::cout << "#" << i << ": " << m_files[i] << std::endl;
115 }
116
117 int Tcov2Lcov::validate() const
118 {
119 xmlInitParser();
120 xmlLineNumbersDefault(1);
121
122 xmlSchemaParserCtxtPtr ctxt = xmlSchemaNewParserCtxt(m_xsd_file.c_str());
123 xmlSchemaSetParserErrors(ctxt, (xmlSchemaValidityErrorFunc)fprintf,
124 (xmlSchemaValidityWarningFunc)fprintf, stderr);
125 // valgrind reports `Conditional jump or move depends on uninitialised
126 // value(s)' even for xmllint when using xmlSchemaParse().
127 xmlSchemaPtr schema = xmlSchemaParse(ctxt);
128 xmlSchemaFreeParserCtxt(ctxt);
129 if (schema == NULL) {
130 xmlSchemaCleanupTypes();
131 xmlCleanupParser();
132 xmlMemoryDump();
133 return -1;
134 }
135 int ret_val = 0;
136 for (size_t i = 0; i < m_files.size(); ++i) {
137 const char *xml_file_name = m_files[i].c_str();
138 xmlDocPtr doc = xmlReadFile(xml_file_name, NULL, 0);
139 if (doc == NULL) {
140 std::cerr << "Could not parse `" << xml_file_name << "'" << std::endl;
141 ret_val = -1;
142 continue;
143 } else {
144 xmlSchemaValidCtxtPtr xml_ctxt = xmlSchemaNewValidCtxt(schema);
145 xmlSchemaSetValidErrors(xml_ctxt, (xmlSchemaValidityErrorFunc)fprintf,
146 (xmlSchemaValidityWarningFunc)fprintf, stderr);
147 int valid = xmlSchemaValidateDoc(xml_ctxt, doc);
148 if (valid != 0) {
149 std::cerr << "`" << xml_file_name << "' fails to validate" << std::endl;
150 ret_val = -1;
151 }
152 xmlSchemaFreeValidCtxt(xml_ctxt);
153 xmlFreeDoc(doc);
154 }
155 }
156 xmlSchemaFree(schema);
157 xmlSchemaCleanupTypes();
158 xmlCleanupParser();
159 xmlMemoryDump();
160 return ret_val;
161 }
162
163 const char *get_next_attr(const xmlTextReaderPtr& reader)
164 {
165 xmlTextReaderMoveToNextAttribute(reader);
166 return (const char *)xmlTextReaderConstValue(reader);
167 }
168
169 int Tcov2Lcov::merge()
170 {
171 for (size_t i = 0; i < m_files.size(); ++i) {
172 std::string& file_name = m_files[i];
173 xmlInitParser(); // Avoid weird memory leaks...
174 xmlTextReaderPtr reader = xmlReaderForFile(file_name.c_str(), NULL, 0);
175 if (reader == NULL) {
176 std::cerr << "Unable to open `" << file_name << "'" << std::endl;
177 return -1;
178 }
179 std::string current_file_name;
180 while (xmlTextReaderRead(reader)) {
181 switch (xmlTextReaderNodeType(reader)) {
182 case XML_READER_TYPE_ELEMENT: {
183 std::string elem((const char *)xmlTextReaderConstName(reader));
184 if (elem == "version") {
185 int major = atoi(get_next_attr(reader));
186 int minor = atoi(get_next_attr(reader));
187 if (m_ver_major == -1) m_ver_major = major;
188 if (m_ver_minor == -1) m_ver_minor = minor;
189 if (m_ver_major != major || m_ver_minor != minor) {
190 std::cerr << "Version mismatch in `" << file_name << "' ("
191 << major << "." << minor << " vs. " << m_ver_major
192 << "." << m_ver_minor << ")" << std::endl;
193 xmlFreeTextReader(reader);
194 xmlCleanupParser();
195 xmlMemoryDump();
196 return -1;
197 }
198 } else if (elem == "file") {
199 current_file_name = get_next_attr(reader);
200 if (m_data.count(current_file_name) == 0)
201 m_data[current_file_name] = new TcovData();
202 } else if (elem == "function") {
203 std::string function_name(get_next_attr(reader));
204 int function_count = atoi(get_next_attr(reader));
205 m_data[current_file_name]->inc_function(function_name, function_count);
206 } else if (elem == "line") {
207 int line_no = atoi(get_next_attr(reader));
208 int line_count = atoi(get_next_attr(reader));
209 m_data[current_file_name]->inc_line(line_no, line_count);
210 }
211 xmlTextReaderMoveToElement(reader);
212 continue;
213 }
214 default:
215 // Ignore.
216 break;
217 }
218 }
219 xmlFreeTextReader(reader);
220 xmlCleanupParser();
221 xmlMemoryDump();
222 }
223 return 0;
224 }
225
226 int Tcov2Lcov::generate()
227 {
228 struct stat st;
229 if (!(stat(m_code_base.c_str(), &st) == 0 && S_ISDIR(st.st_mode))) {
230 std::cerr << "Invalid code base `" << m_code_base << "'" << std::endl;
231 return -1;
232 }
233 std::ofstream output(m_output_file.c_str());
234 if (!output.is_open()) {
235 std::cerr << "Output file `" << m_output_file << "' cannot be opened" << std::endl;
236 return -1;
237 }
238 output << "TN:" << std::endl;
239 std::map<std::string, TcovData *>::const_iterator i = m_data.begin();
240 for (; i != m_data.end(); ++i) {
241 output << "SF:" << m_code_base << P_SEP << (*i).first << std::endl;
242 const TcovData *data = (*i).second;
243 const std::map<std::string, int>& functions = data->get_functions();
244 const std::map<int, int>& lines = data->get_lines();
245 std::map<std::string, int>::const_iterator j = functions.begin();
246 std::map<int, int>::const_iterator k = lines.begin();
247 for (; j != functions.end(); ++j) // All functions.
248 output << "FN:0," << (*j).first << std::endl;
249 for (j = functions.begin(); j != functions.end(); ++j) // All functions counted.
250 output << "FNDA:" << (*j).second << "," << (*j).first << std::endl;
251 for (; k != lines.end(); ++k) // All lines counted.
252 output << "DA:" << (*k).first << "," << (*k).second << std::endl;
253 output << "end_of_record" << std::endl;
254 }
255 output.close();
256 return 0;
257 }
258
259 static void print_product_info()
260 {
261 std::cerr << "TCD to LCOV Converter for the TTCN-3 Test Executor, version "
262 << PRODUCT_NUMBER << std::endl;
263 }
264
265 static void print_version()
266 {
267 std::cerr << "Product number: " << PRODUCT_NUMBER << std::endl
268 << "Build date: " << __DATE__ << " " << __TIME__ << std::endl
269 << "Compiled with: " << C_COMPILER_VERSION << std::endl << std::endl
270 << COPYRIGHT_STRING << std::endl << std::endl;
271 }
272
273 static void print_help(const char *name)
274 {
275 std::cerr << "Usage: " << name << " [-hv] [-b dir] [-d dir] [-o file] [-x xsd] " << std::endl
276 << " -b dir\tSet code base for source files" << std::endl
277 << " -d dir\tCollect XML/TCD files from `dir'" << std::endl
278 << " -h\t\tPrint help" << std::endl
279 << " -o file\tPlace of the output file" << std::endl
280 << " -v\t\tShow version" << std::endl
281 << " -x xsd\tValidate all XMLs/TCDs against this `xsd'" << std::endl;
282 }
283
284 int main(int argc, char *argv[])
285 {
286 // Unfortunately, getopt_long() and :: for optional parameters are GNU extensions and not present on Solaris...
287 // -b --dir: Set code base for source files (_optional_)
288 // -d --dir: Collect XML/TCD files from, default is `.' the current directory (_optional_)
289 // -o --output: Place of the output, default is `./output.info' (_optional_)
290 // -x --xsd: Validate all XMLs/TCDs against this XSD (_optional_)
291 // -h --help: Display help (_optional_)
292 const char *code_base = NULL, *input_dir = NULL, *output_file = NULL, *xsd_file = NULL;
293 int c;
294 while ((c = getopt(argc, argv, "b:d:ho:vx:")) != -1) {
295 switch (c) {
296 case 'b':
297 code_base = optarg;
298 break;
299 case 'd':
300 input_dir = optarg;
301 break;
302 case 'h':
303 print_product_info();
304 print_help(argv[0]);
305 return EXIT_SUCCESS;
306 case 'o':
307 output_file = optarg;
308 break;
309 case 'v':
310 print_product_info();
311 print_version();
312 #ifdef LICENSE
313 print_license_info();
314 #endif
315 return EXIT_SUCCESS;
316 case 'x':
317 xsd_file = optarg;
318 break;
319 default:
320 print_help(argv[0]);
321 return EXIT_FAILURE;
322 }
323 }
324 bool param_error = false;
325 #ifndef PATH_MAX
326 #define PATH_MAX 1024
327 #endif
328 char cwd[PATH_MAX];
329 memset(cwd, 0, PATH_MAX);
330 if (!code_base) {
331 if (!getcwd(cwd, PATH_MAX)) {
332 std::cerr << "Unable to get current working directory for `-b'" << std::endl;
333 param_error = true;
334 } else code_base = cwd;
335 }
336 // Leave it relative.
337 if (!input_dir) input_dir = ".";
338 if (!output_file) output_file = "output.info";
339 std::string xsd_file_str;
340 if (!xsd_file) {
341 const char *ttcn3_dir = getenv("TTCN3_DIR");
342 if (!ttcn3_dir) {
343 std::cerr << "$TTCN3_DIR environment variable is not set" << std::endl;
344 param_error = true;
345 } else {
346 xsd_file_str = std::string(ttcn3_dir) + P_SEP + std::string("include") + P_SEP + std::string("tcov.xsd");
347 xsd_file = xsd_file_str.c_str();
348 }
349 }
350 if (param_error) {
351 print_help(argv[0]);
352 return EXIT_FAILURE;
353 }
354
355 #ifdef LICENSE
356 {
357 init_openssl();
358 license_struct lstr;
359 load_license (&lstr);
360 int license_valid = verify_license(&lstr);
361 free_license (&lstr);
362 free_openssl();
363 if (!license_valid) {
364 exit(EXIT_FAILURE);
365 }
366 }
367 #endif
368
369 Tcov2Lcov t2l(code_base, input_dir, output_file, xsd_file);
370 int num_files = t2l.collect();
371 if (num_files < 0) {
372 std::cerr << "Directory `" << input_dir << "' doesn't exist" << std::endl;
373 return EXIT_FAILURE;
374 } else if (num_files == 0) {
375 std::cerr << "No data files were found in the given directory `"
376 << input_dir << "'" << std::endl;
377 return EXIT_FAILURE;
378 }
379 // Initialize the library and check potential ABI mismatches between
380 // the between the version it was compiled for and the actual shared
381 // library used.
382 LIBXML_TEST_VERSION
383 if (t2l.validate() < 0) {
384 std::cerr << "Error while validating XML/TCD files" << std::endl;
385 return EXIT_FAILURE;
386 }
387 if (t2l.merge() < 0) {
388 std::cerr << "Error while merging XML/TCD files" << std::endl;
389 return EXIT_FAILURE;
390 }
391 if (t2l.generate() < 0) {
392 std::cerr << "Error while generating output file `" << output_file << "'"
393 << std::endl;
394 return EXIT_FAILURE;
395 }
396 return EXIT_SUCCESS;
397 }
398
399 reffer::reffer(const char*) {}
This page took 0.040169 seconds and 5 git commands to generate.