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