예제 #1
0
    def test_old_ldlogger(self):
        """
        Test log file parsing escape behaviour with pre-2017 Q2 LD-LOGGER.
        """
        logfile = os.path.join(self.__test_files, "ldlogger-old.json")

        # LD-LOGGER before http://github.com/Ericsson/codechecker/pull/631
        # used an escape mechanism that, when parsed by the log parser via
        # shlex, made CodeChecker parse arguments with multiword string
        # literals in them be considered as "file" (instead of compile option),
        # eventually ignored by the command builder, thus lessening analysis
        # accuracy, as defines were lost.
        #
        # Logfile contains "-DVARIABLE="some value"".
        #
        # There is no good way to back-and-forth convert in log_parser or
        # option_parser, so here we aim for a non-failing stalemate of the
        # define being considered a file and ignored, for now.

        build_action = log_parser.parse_log(logfile, ParseLogOptions())[0]
        results = option_parser.parse_options(build_action.original_command)

        self.assertEqual(' '.join(results.files),
                         r'"-DVARIABLE="some value"" /tmp/a.cpp')
        self.assertEqual(len(build_action.analyzer_options), 0)
예제 #2
0
    def test_compile_with_include_paths(self):
        """
        sysroot should be detected as compiler option because it is needed
        for the analyzer too to search for headers.
        """
        source_files = ["main.cpp", "test.cpp"]
        compiler_options = [
            "-std=c++11", "-include/include/myheader.h",
            "-include /include/myheader2.h", "--include",
            "/include/myheader3.h", "--sysroot", "/home/sysroot",
            "--sysroot=/home/sysroot3", "-isysroot /home/isysroot",
            "-isysroot/home/isysroot2", "-I/home/test", "-I /home/test2",
            "-idirafter /dirafter1", "-idirafter/dirafter2"
        ]
        linker_options = ["-L/home/test_lib", "-lm"]
        build_cmd = "g++ -o myapp " + \
                    ' '.join(compiler_options) + ' ' + \
                    ' '.join(linker_options) + ' ' + \
                    ' '.join(source_files)

        res = option_parser.parse_options(build_cmd)
        print(res)
        co_no_space = []
        for c in compiler_options:
            co_no_space.append(c.replace(" ", ""))
        print(set(co_no_space))
        print(set(res.compile_opts))
        self.assertTrue(set(source_files) == set(res.files))
        self.assertTrue(set(co_no_space) == set(res.compile_opts))
        self.assertTrue(set(linker_options) == set(res.link_opts))
        self.assertEqual(ActionType.COMPILE, res.action)
예제 #3
0
 def test_link_only_multiple_files(self):
     """
     Should be link if only object files are in the command.
     """
     build_cmd = "g++ -o fubar foo.o main.o bar.o -lm"
     res = option_parser.parse_options(build_cmd)
     print(res)
     self.assertEquals(ActionType.LINK, res.action)
예제 #4
0
 def test_ignore_flags(self):
     ignore = [
         "-Werror", "-MT hello", "-M", "-fsyntax-only",
         "-mfloat-gprs=double", "-mfloat-gprs=yes"
     ]
     build_cmd = "g++ {} main.cpp".format(' '.join(ignore))
     res = option_parser.parse_options(build_cmd)
     self.assertEqual(res.compile_opts, ["-fsyntax-only"])
예제 #5
0
 def test_ignore_flags(self):
     ignore = [
         "-Werror", "-MT hello", "-M", "-fsyntax-only",
         "-mfloat-gprs=double", "-mfloat-gprs=yes", "-mabi=spe",
         "-mabi=eabi", '-Xclang', '-mllvm', '-Xclang',
         '-instcombine-lower-dbg-declare=0'
     ]
     build_cmd = "g++ {} main.cpp".format(' '.join(ignore))
     res = option_parser.parse_options(build_cmd)
     self.assertEqual(res.compile_opts, ["-fsyntax-only"])
예제 #6
0
    def test_compile_onefile(self):
        """
        Test the compiler command of one file.
        """
        source_files = ["main.cpp"]
        build_cmd = "g++ -c " + ' '.join(source_files)

        res = option_parser.parse_options(build_cmd)
        print(res)
        self.assertTrue(set(source_files) == set(res.files))
        self.assertEquals(ActionType.COMPILE, res.action)
예제 #7
0
    def test_build_multiplefiles(self):
        """
        Test the build command of multiple files.
        """
        source_files = ["lib.cpp", "main.cpp"]
        build_cmd = "g++ -o main " + ' '.join(source_files)

        res = option_parser.parse_options(build_cmd)
        print(res)
        self.assertTrue(set(source_files) == set(res.files))
        self.assertEquals(ActionType.COMPILE, res.action)
예제 #8
0
    def test_build_onefile(self):
        """
        Test the build command of a simple file.
        """
        source_files = ["main.cpp"]
        build_cmd = "g++ -o main " + ' '.join(source_files)

        res = option_parser.parse_options(build_cmd)
        print(res)
        self.assertTrue(set(source_files) == set(res.files))
        self.assertTrue(ActionType.COMPILE, res.action)
        self.assertEquals(0, len(res.compile_opts))
예제 #9
0
    def test_preprocess_onefile(self):
        """
        Test the preprocess command of one file.
        """
        source_files = ["main.c"]
        build_cmd = "gcc -E " + ' '.join(source_files)

        print(build_cmd)
        res = option_parser.parse_options(build_cmd)
        print(res)

        self.assertTrue(set(source_files) == set(res.files))
        self.assertEqual(ActionType.PREPROCESS, res.action)
예제 #10
0
    def test_compiler_toolchain(self):
        """
        Test if compiler toolchain is parsed and forwarded properly.
        """
        source_files = ["main.cpp"]
        compiler_options = ["--gcc-toolchain=/home/user/mygcctoolchain"]
        build_cmd = "g++ -c " + \
                    ' '.join(compiler_options) + ' ' + \
                    ' '.join(source_files)

        res = option_parser.parse_options(build_cmd)
        print(res)
        self.assertTrue(set(compiler_options) == set(res.compile_opts))
        self.assertEqual(ActionType.COMPILE, res.action)
예제 #11
0
    def test_compile_optimized(self):
        """
        Test if the compilation arguments is
        detected correctly from the command line.
        """
        source_files = ["main.cpp"]
        compiler_options = ["-O3"]
        build_cmd = "g++ -c " + \
                    ' '.join(compiler_options) + ' ' + \
                    ' '.join(source_files)

        res = option_parser.parse_options(build_cmd)
        print(res)
        self.assertTrue(set(compiler_options) == set(res.compile_opts))
        self.assertEqual(ActionType.COMPILE, res.action)
예제 #12
0
    def test_compile_arch(self):
        """
        Test if the compilation architecture is
        detected correctly from the command line.
        """
        source_files = ["main.c"]
        arch = 'x86_64'
        build_cmd = "gcc -c -arch " + arch + " " + ' '.join(source_files)

        print(build_cmd)
        res = option_parser.parse_options(build_cmd)
        print(res)

        self.assertTrue(set(source_files) == set(res.files))
        self.assertEqual(arch, res.arch)
        self.assertEqual(ActionType.COMPILE, res.action)
예제 #13
0
    def test_compile_lang(self):
        """
        Test if the compilation language is
        detected correctly from the command line.
        """
        source_files = ["main.c"]
        lang = 'c'
        build_cmd = "gcc -c -x " + lang + " " + ' '.join(source_files)

        print(build_cmd)
        res = option_parser.parse_options(build_cmd)
        print(res)

        self.assertTrue(set(source_files) == set(res.files))
        self.assertEqual(lang, res.lang)
        self.assertEqual(ActionType.COMPILE, res.action)
예제 #14
0
    def test_link_with_include_paths(self):
        """
        Should be link if only object files are in the command.
        """
        object_files = ["foo.o", "main.o", "bar.o"]
        compiler_options = [
            "--sysroot", "/home/sysroot", "-isysroot/home/isysroot",
            "-I/home/test"
        ]
        linker_options = ["-L/home/test_lib", "-lm"]
        build_cmd = "g++ -o fubar " + \
                    ' '.join(compiler_options) + ' ' + \
                    ' '.join(linker_options) + ' ' + \
                    ' '.join(object_files)

        res = option_parser.parse_options(build_cmd)
        print(res)
        self.assertTrue(set(object_files) == set(res.files))
        self.assertTrue(set(compiler_options) == set(res.compile_opts))
        self.assertTrue(set(linker_options) == set(res.link_opts))
        self.assertEqual(ActionType.LINK, res.action)
예제 #15
0
    def test_compile_with_include_paths(self):
        """
        sysroot should be detected as compiler option because it is needed
        for the analyzer too to search for headers.
        """
        source_files = ["main.cpp", "test.cpp"]
        compiler_options = ["-sysroot", "/home/sysroot",
                            "-isysroot", "/home/isysroot",
                            "-I/home/test"]
        linker_options = ["-L/home/test_lib", "-lm"]
        build_cmd = "g++ -o myapp " + \
                    ' '.join(compiler_options) + ' ' + \
                    ' '.join(linker_options) + ' ' + \
                    ' '.join(source_files)

        res = option_parser.parse_options(build_cmd)
        print(res)
        self.assertTrue(set(source_files) == set(res.files))
        self.assertTrue(set(compiler_options) == set(res.compile_opts))
        self.assertTrue(set(linker_options) == set(res.link_opts))
        self.assertEqual(ActionType.COMPILE, res.action)
예제 #16
0
def parse_compile_commands_json(logfile, add_compiler_defaults=False):
    import json
    LOG.debug('parse_compile_commands_json: ' + str(add_compiler_defaults))

    actions = []
    filtered_build_actions = {}

    logfile.seek(0)
    data = json.load(logfile)

    compiler_defines = {}
    compiler_includes = {}

    counter = 0
    for entry in data:
        sourcefile = entry['file']
        lang = option_parser.get_language(sourcefile[sourcefile.rfind('.'):])

        if not lang:
            continue

        action = build_action.BuildAction(counter)

        command = entry['command']
        results = option_parser.parse_options(command)

        action.original_command = command
        action.analyzer_options = results.compile_opts
        action.lang = results.lang
        action.target = results.arch

        # store the compiler built in include paths
        # and defines
        if add_compiler_defaults and results.compiler:
            if not (results.compiler in compiler_defines):
                compiler_defines[results.compiler] = \
                    get_compiler_defines(results.compiler)
                compiler_includes[results.compiler] = \
                    get_compiler_includes(results.compiler)
            action.compiler_defines = compiler_defines[results.compiler]
            action.compiler_includes = compiler_includes[results.compiler]

        if results.action == option_parser.ActionType.COMPILE or \
           results.action == option_parser.ActionType.LINK:
            action.skip = False

        # TODO: check arch.
        action.directory = entry['directory']
        action.sources = sourcefile
        # Filter out duplicate compilation commands.
        unique_key = action.cmp_key
        if filtered_build_actions.get(unique_key) is None:
            filtered_build_actions[unique_key] = action

        del action
        counter += 1

    for ba_hash, ba in filtered_build_actions.items():
        actions.append(ba)

    return actions
예제 #17
0
def parse_compile_commands_json(logfile, add_compiler_defaults=False):
    import json
    LOG.debug('parse_compile_commands_json: ' + str(add_compiler_defaults))

    actions = []
    filtered_build_actions = {}

    logfile.seek(0)
    data = json.load(logfile)

    compiler_defines = {}
    compiler_includes = {}

    counter = 0
    for entry in data:
        sourcefile = entry['file']

        if not os.path.isabs(sourcefile):
            # Newest versions of intercept-build can create the 'file' in the
            # JSON Compilation Database as a relative path.
            sourcefile = os.path.join(os.path.abspath(entry['directory']),
                                      sourcefile)

        lang = option_parser.get_language(sourcefile[sourcefile.rfind('.'):])

        if not lang:
            continue

        action = build_action.BuildAction(counter)
        if 'command' in entry:
            command = entry['command']

            # Old versions of intercept-build (confirmed to those shipping
            # with upstream clang-5.0) do escapes in another way:
            # -DVARIABLE="a b" becomes -DVARIABLE=\"a b\" in the output.
            # This would be messed up later on by options_parser, so need a
            # fix here. (Should be removed once we are sure noone uses this
            # intercept-build anymore!)
            if r'\"' in command:
                command = command.replace(r'\"', '"')
        elif 'arguments' in entry:
            # Newest versions of intercept-build create an argument vector
            # instead of a command string.
            command = ' '.join(entry['arguments'])
        else:
            raise KeyError("No valid 'command' or 'arguments' entry found!")
        results = option_parser.parse_options(command)

        action.original_command = command
        action.analyzer_options = results.compile_opts

        action.lang = results.lang
        action.target = results.arch

        # Store the compiler built in include paths and defines.
        if add_compiler_defaults and results.compiler:
            if not (results.compiler in compiler_defines):
                # Fetch defaults from the compiler,
                # make sure we use the correct architecture.
                extra_opts = []
                for regex in COMPILE_OPTS_FWD_TO_DEFAULTS_GETTER:
                    pattern = re.compile(regex)
                    for comp_opt in action.analyzer_options:
                        if re.match(pattern, comp_opt):
                            extra_opts.append(comp_opt)

                compiler_defines[results.compiler] = \
                    get_compiler_defines(results.compiler, extra_opts)
                compiler_includes[results.compiler] = \
                    get_compiler_includes(results.compiler, extra_opts)
            action.compiler_defines = compiler_defines[results.compiler]
            action.compiler_includes = compiler_includes[results.compiler]

        if results.action == option_parser.ActionType.COMPILE or \
           results.action == option_parser.ActionType.LINK:
            action.skip = False

        # TODO: Check arch.
        action.directory = entry['directory']
        action.sources = sourcefile
        # Filter out duplicate compilation commands.
        unique_key = action.cmp_key
        if filtered_build_actions.get(unique_key) is None:
            filtered_build_actions[unique_key] = action

        del action
        counter += 1

    for ba_hash, ba in filtered_build_actions.items():
        actions.append(ba)

    return actions
예제 #18
0
def parse_compile_commands_json(logfile, parseLogOptions):
    """
    logfile: is a compile command json
    """

    output_path = parseLogOptions.output_path
    if output_path is not None:
        remove_file_if_exists(
            os.path.join(output_path, compiler_includes_dump_file))
        remove_file_if_exists(
            os.path.join(output_path, compiler_target_dump_file))

    actions = []
    filtered_build_actions = {}

    data = json.load(logfile)

    compiler_includes = {}
    compiler_target = {}

    counter = 0
    for entry in data:
        sourcefile = entry['file']

        if not os.path.isabs(sourcefile):
            # Newest versions of intercept-build can create the 'file' in the
            # JSON Compilation Database as a relative path.
            sourcefile = os.path.join(os.path.abspath(entry['directory']),
                                      sourcefile)

        lang = option_parser.get_language(sourcefile[sourcefile.rfind('.'):])

        if not lang:
            continue

        action = build_action.BuildAction(counter)
        if 'command' in entry:
            command = entry['command']

            # Old versions of intercept-build (confirmed to those shipping
            # with upstream clang-5.0) do escapes in another way:
            # -DVARIABLE="a b" becomes -DVARIABLE=\"a b\" in the output.
            # This would be messed up later on by options_parser, so need a
            # fix here. (Should be removed once we are sure noone uses this
            # intercept-build anymore!)
            if r'\"' in command:
                command = command.replace(r'\"', '"')
        elif 'arguments' in entry:
            # Newest versions of intercept-build create an argument vector
            # instead of a command string.
            command = ' '.join(entry['arguments'])
        else:
            raise KeyError("No valid 'command' or 'arguments' entry found!")
        results = option_parser.parse_options(command)

        action.original_command = command

        # If the original include directory could not be found
        # in the filesystem, it is possible that it was provided
        # relative to the working directory in the compile json.
        compile_opts = results.compile_opts
        for i, opt in enumerate(compile_opts):
            if opt.startswith('-I'):
                inc_dir = opt[2:].strip()
                if not os.path.isdir(inc_dir):
                    compile_opts[i] = '-I' + \
                        os.path.join(entry['directory'], inc_dir)

        action.analyzer_options = compile_opts

        action.lang = results.lang
        action.target = results.arch
        action.output = results.output

        add_compiler_defaults = True

        # With gcc-toolchain a non default compiler toolchain can be set.
        # Clang will search for include paths and libraries based on the
        # gcc-toolchain parameter.
        # Detecting extra include paths from the host compiler could
        # conflict with this.

        # For example if the compiler in the compile command is clang
        # and gcc-toolchain is set we will get the include paths
        # for clang and not for the compiler set in gcc-toolchain.
        # This can cause missing headers during the analysis.

        toolchain = gcc_toolchain.toolchain_in_args(action.analyzer_options)
        if toolchain:
            add_compiler_defaults = False

        # Store the compiler built in include paths and defines.
        if add_compiler_defaults and results.compiler:
            if not (results.compiler in compiler_includes):
                # Fetch defaults from the compiler,
                # make sure we use the correct architecture.
                extra_opts = []
                for regex in COMPILE_OPTS_FWD_TO_DEFAULTS_GETTER:
                    pattern = re.compile(regex)
                    for comp_opt in action.analyzer_options:
                        if re.match(pattern, comp_opt):
                            extra_opts.append(comp_opt)

                compiler_includes[results.compiler] = \
                    get_compiler_includes(parseLogOptions, results.compiler,
                                          results.lang, results.compile_opts,
                                          extra_opts)

            if not (results.compiler in compiler_target):
                compiler_target[results.compiler] = \
                    get_compiler_target(parseLogOptions, results.compiler)

            action.compiler_includes = compiler_includes[results.compiler]
            action.target = compiler_target[results.compiler]

        if results.action != option_parser.ActionType.COMPILE:
            action.skip = True

        # TODO: Check arch.
        action.directory = entry['directory']
        action.sources = sourcefile
        # Filter out duplicate compilation commands.
        unique_key = action.cmp_key
        if filtered_build_actions.get(unique_key) is None:
            filtered_build_actions[unique_key] = action

        del action
        counter += 1

    for _, ba in filtered_build_actions.items():
        actions.append(ba)
    return actions