1#!/usr/bin/env python3
2
3import os
4from . import settings, utils, tag_database, cppcheck_analysis
5
6class ParseTagPhaseError(Exception):
7    pass
8
9class BuildPhaseError(Exception):
10    pass
11
12class CleanPhaseError(Exception):
13    pass
14
15
16def parse_xen_tags():
17    # Load the database for the Xen tags
18    subs_list = tag_database.load_tag_database(
19        settings.analysis_tool,
20        [settings.repo_dir + "/docs/misra/safe.json"]
21    )
22    subs_list = tag_database.load_tag_database(
23        settings.analysis_tool,
24        [settings.repo_dir + "/docs/misra/false-positive-{}.json"
25                                .format(settings.analysis_tool)],
26        subs_list,
27        "false-positive"
28    )
29
30    # Create outdir if it doesn't exists
31    os.makedirs(settings.outdir, exist_ok=True)
32
33    # The following lambda function will return a file if it contains lines with
34    # a comment containing "SAF-<number>-{safe|false-positive-<tool>}" on a
35    # single line.
36    grep_action = lambda x: utils.grep(x,
37                                    tag_database.get_xen_tag_comment_regex(
38                                                        settings.analysis_tool)
39    )
40    # Look for a list of .h/.c files that matches the condition above
41    parse_file_list = utils.recursive_find_file(settings.xen_dir, r'.*\.[ch]$',
42                                                grep_action)
43
44    for entry in parse_file_list:
45        file = entry["file"]
46        bkp_file = file + ".safparse"
47        if os.path.isfile(bkp_file):
48            raise ParseTagPhaseError(
49                "Found {}, please check the integrity of {}"
50                    .format(bkp_file,file)
51                )
52        os.rename(file, bkp_file)
53        time_bkp_file = os.stat(bkp_file)
54        # Create <file> from <file>.safparse but with the Xen tag parsed
55        try:
56            tag_database.substitute_tags(settings.analysis_tool, bkp_file, entry,
57                                         subs_list)
58        except Exception as e:
59            raise ParseTagPhaseError("{}".format(e))
60        finally:
61            # Set timestamp for file equal to bkp_file, so that if the file is
62            # modified during the process by the user, we can catch it
63            os.utime(file, (time_bkp_file.st_atime, time_bkp_file.st_mtime))
64
65
66def build_xen():
67    utils.invoke_command(
68            "make -C {} {} {} build"
69                .format(settings.xen_dir, settings.make_forward_args,
70                        cppcheck_analysis.cppcheck_extra_make_args),
71            False, BuildPhaseError,
72            "Build error occured when running:\n{}"
73        )
74
75
76def clean_analysis_artifacts():
77    safparse_files = utils.recursive_find_file(settings.xen_dir,
78                                               r'.*.safparse$')
79    for original_file in safparse_files:
80        # This commands strips the .safparse extension, leaving <file>
81        parsed_file_path = os.path.splitext(original_file)[0]
82        mtime_original_file = os.stat(original_file).st_mtime
83        mtime_parsed_file = os.stat(parsed_file_path).st_mtime
84        if mtime_original_file != mtime_parsed_file:
85            return CleanPhaseError(
86                    "The file {} was modified during the analysis "
87                    "procedure, it is impossible now to restore from the "
88                    "content of {}, please handle it manually"
89                    .format(parsed_file_path, original_file)
90                )
91        # Replace <file>.safparse to <file>
92        os.replace(original_file, parsed_file_path)
93