예제 #1
0
def ninja_check_all_report(step: Step, _: Report, filter_output: bool):
    print('Full log will be available in Artifacts "ninja-check-all.log"',
          flush=True)
    step.reproduce_commands.append('ninja check-all')
    with open(f'{artifacts_dir}/ninja-check-all.log', 'wb') as f:
        w = sys.stdout.buffer.write
        if filter_output:
            r = re.compile(
                r'^(\[.*] (Building|Linking|Generating)|(PASS|XFAIL|UNSUPPORTED):)'
            )
            w = partial(if_not_matches,
                        write=sys.stdout.buffer.write,
                        regexp=r)
        rc = watch_shell(partial(tee, write1=w, write2=f.write),
                         partial(tee,
                                 write1=sys.stderr.buffer.write,
                                 write2=f.write),
                         'ninja check-all',
                         cwd=build_dir)
        logging.debug(f'ninja check-all: returned {rc}')
        step.set_status_from_exit_code(rc)
    test_results_report.run(build_dir, 'test-results.xml', step, report)
    if not step.success:
        message = 'tests failed'
        f = report.test_stats['fail']
        if f == 1:
            message = '1 test failed'
        if f > 1:
            message = f'{f} tests failed'
        report.add_artifact(artifacts_dir, 'ninja-check-all.log', message)
def cmake_report(projects: str, step: Step, _: Report):
    global build_dir
    cmake_result, build_dir, cmake_artifacts = run_cmake.run(
        projects, os.getcwd())
    for file in cmake_artifacts:
        if os.path.exists(file):
            shutil.copy2(file, artifacts_dir)
    step.set_status_from_exit_code(cmake_result)
def ninja_all_report(step: Step, _: Report):
    print('Full log will be available in Artifacts "ninja-all.log"',
          flush=True)
    r = subprocess.run(
        f'ninja all | '
        f'tee {artifacts_dir}/ninja-all.log | '
        f'grep -vE "\\[.*] (Building|Linking|Linting|Copying|Generating|Creating)"',
        shell=True,
        cwd=build_dir)
    logging.debug(f'ninja all: returned {r.returncode}, stderr: "{r.stderr}"')
    step.set_status_from_exit_code(r.returncode)
예제 #4
0
def run_step(name: str, report: Report, thunk: Callable[[Step, Report],
                                                        None]) -> Step:
    start = time.time()
    print(f'---  {name}', flush=True)  # New section in Buildkite log.
    step = Step()
    step.name = name
    thunk(step, report)
    step.duration = time.time() - start
    # Expand section if it failed.
    if not step.success:
        print('^^^ +++', flush=True)
    report.steps.append(step)
    return step
def ninja_check_all_report(step: Step, _: Report):
    print('Full log will be available in Artifacts "ninja-check-all.log"',
          flush=True)
    r = subprocess.run(
        f'ninja check-all | tee {artifacts_dir}/ninja-check-all.log | '
        f'grep -vE "^\\[.*] (Building|Linking)" | '
        f'grep -vE "^(PASS|XFAIL|UNSUPPORTED):"',
        shell=True,
        cwd=build_dir)
    logging.debug(
        f'ninja check-all: returned {r.returncode}, stderr: "{r.stderr}"')
    step.set_status_from_exit_code(r.returncode)
    test_results_report.run(build_dir, 'test-results.xml', step, report)
def run(base_commit, ignore_config, step: Optional[Step],
        report: Optional[Report]):
    """Apply clang-format and return if no issues were found."""
    if report is None:
        report = Report()  # For debugging.
    if step is None:
        step = Step()  # For debugging.
    r, patch = get_diff(base_commit)
    if not r:
        step.success = False
        return
    add_artifact = False
    patches = unidiff.PatchSet(patch)
    ignore_lines = []
    if ignore_config is not None and os.path.exists(ignore_config):
        ignore_lines = open(ignore_config, 'r').readlines()
    ignore = pathspec.PathSpec.from_lines(
        pathspec.patterns.GitWildMatchPattern, ignore_lines)
    patched_file: unidiff.PatchedFile
    success = True
    for patched_file in patches:
        add_artifact = True
        if ignore.match_file(patched_file.source_file) or ignore.match_file(
                patched_file.target_file):
            logging.info(f'patch of {patched_file.patch_info} is ignored')
            continue
        hunk: unidiff.Hunk
        for hunk in patched_file:
            lines = [str(x) for x in hunk]
            success = False
            m = 10  # max number of lines to report.
            description = 'please reformat the code\n```\n'
            n = len(lines)
            cut = n > m + 1
            if cut:
                lines = lines[:m]
            description += ''.join(lines) + '\n```'
            if cut:
                description += f'\n{n - m} diff lines are omitted. See full path.'
            report.add_lint({
                'name': 'clang-format',
                'severity': 'autofix',
                'code': 'clang-format',
                'path': patched_file.source_file,
                'line': hunk.source_start,
                'char': 1,
                'description': description,
            })
    if add_artifact:
        patch_file = 'clang-format.patch'
        with open(patch_file, 'w') as f:
            f.write(patch)
        report.add_artifact(os.getcwd(), patch_file, 'clang-format')
    if not success:
        step.success = False
        step.messages.append(
            'Please format your changes with clang-format by running `git-clang-format HEAD^` or applying patch.'
        )
    logging.debug(f'report: {report}')
    logging.debug(f'step: {step}')
예제 #7
0
def ninja_all_report(step: Step, _: Report, filter_output: bool):
    print('Full log will be available in Artifacts "ninja-all.log"',
          flush=True)
    step.reproduce_commands.append('ninja all')
    with open(f'{artifacts_dir}/ninja-all.log', 'wb') as f:
        w = sys.stdout.buffer.write
        if filter_output:
            r = re.compile(
                r'^\[.*] (Building|Linking|Linting|Copying|Generating|Creating)'
            )
            w = partial(if_not_matches,
                        write=sys.stdout.buffer.write,
                        regexp=r)
        rc = watch_shell(partial(tee, write1=w, write2=f.write),
                         partial(tee,
                                 write1=sys.stderr.buffer.write,
                                 write2=f.write),
                         'ninja all',
                         cwd=build_dir)
        logging.debug(f'ninja all: returned {rc}')
        step.set_status_from_exit_code(rc)
        if not step.success:
            report.add_artifact(artifacts_dir, 'ninja-all.log', 'build failed')
def run(working_dir: str, test_results: str, step: Optional[Step],
        report: Optional[Report]):
    if report is None:
        report = Report()  # For debugging.
    if step is None:
        step = Step()
    path = os.path.join(working_dir, test_results)
    if not os.path.exists(path):
        logging.warning(f'{path} is not found')
        step.success = False
        step.messages.append(f'test report "{path}" is not found')
        return
    report.add_artifact(working_dir, test_results, 'test results')
    try:
        success = True
        root_node = etree.parse(path)
        for test_case in root_node.xpath('//testcase'):
            test_result = 'pass'
            if test_case.find('failure') is not None:
                test_result = 'fail'
            if test_case.find('skipped') is not None:
                test_result = 'skip'
            report.test_stats[test_result] += 1
            if test_result == 'fail':
                success = False
                failure = test_case.find('failure')
                test_result = {
                    'name': test_case.attrib['name'],
                    'namespace': test_case.attrib['classname'],
                    'result': test_result,
                    'duration': float(test_case.attrib['time']),
                    'details': failure.text
                }
                report.unit.append(test_result)

        msg = f'{report.test_stats["pass"]} tests passed, {report.test_stats["fail"]} failed and ' \
              f'{report.test_stats["skip"]} were skipped.\n'
        if not success:
            step.success = False
            for test_case in report.unit:
                if test_case['result'] == 'fail':
                    msg += f'{test_case["namespace"]}/{test_case["name"]}\n'
    except Exception as e:
        logging.error(e)
        step.messages.append('Parsing of test results failed')
        step.success = False

    logging.debug(f'report: {report}')
    logging.debug(f'step: {step}')
def run(base_commit, ignore_config, step: Optional[Step],
        report: Optional[Report]):
    """Apply clang-format and return if no issues were found."""
    if report is None:
        report = Report()  # For debugging.
    if step is None:
        step = Step()  # For debugging.
    r = subprocess.run(f'git diff -U0 --no-prefix {base_commit}',
                       shell=True,
                       capture_output=True)
    logging.debug(f'git diff {r}')
    diff = r.stdout.decode()
    if ignore_config is not None and os.path.exists(ignore_config):
        ignore = pathspec.PathSpec.from_lines(
            pathspec.patterns.GitWildMatchPattern,
            open(ignore_config, 'r').readlines())
        diff = ignore_diff.remove_ignored(diff.splitlines(keepends=True),
                                          open(ignore_config, 'r'))
        logging.debug(f'filtered diff: {diff}')
    else:
        ignore = pathspec.PathSpec.from_lines(
            pathspec.patterns.GitWildMatchPattern, [])
    p = subprocess.Popen(['clang-tidy-diff', '-p0', '-quiet'],
                         stdout=subprocess.PIPE,
                         stdin=subprocess.PIPE,
                         stderr=subprocess.PIPE)
    step.reproduce_commands.append(
        f'git diff -U0 --no-prefix {base_commit} | clang-tidy-diff -p0')
    a = ''.join(diff)
    logging.info(f'clang-tidy input: {a}')
    out = p.communicate(input=a.encode())[0].decode()
    logging.debug(f'clang-tidy-diff {p}: {out}')
    # Typical finding looks like:
    # [cwd/]clang/include/clang/AST/DeclCXX.h:3058:20: error: ... [clang-diagnostic-error]
    pattern = '^([^:]*):(\\d+):(\\d+): (.*): (.*)'
    add_artifact = False
    logging.debug("cwd", os.getcwd())
    errors_count = 0
    warn_count = 0
    inline_comments = 0
    for line in out.splitlines(keepends=False):
        line = line.strip()
        line = line.replace(os.getcwd() + os.sep, '')
        logging.debug(line)
        if len(line) == 0 or line == 'No relevant changes found.':
            continue
        add_artifact = True
        match = re.search(pattern, line)
        if match:
            file_name = match.group(1)
            line_pos = match.group(2)
            char_pos = match.group(3)
            severity = match.group(4)
            text = match.group(5)
            text += '\n[[{} | not useful]] '.format(
                'https://github.com/google/llvm-premerge-checks/blob/master/docs/clang_tidy.md#warning-is-not-useful'
            )
            if severity in ['warning', 'error']:
                if severity == 'warning':
                    warn_count += 1
                if severity == 'error':
                    errors_count += 1
                if ignore.match_file(file_name):
                    print(
                        '{} is ignored by pattern and no comment will be added'
                        .format(file_name))
                else:
                    inline_comments += 1
                    report.add_lint({
                        'name':
                        'clang-tidy',
                        'severity':
                        'warning',
                        'code':
                        'clang-tidy',
                        'path':
                        file_name,
                        'line':
                        int(line_pos),
                        'char':
                        int(char_pos),
                        'description':
                        '{}: {}'.format(severity, text),
                    })
        else:
            logging.debug('does not match pattern')
    if add_artifact:
        p = 'clang-tidy.txt'
        with open(p, 'w') as f:
            f.write(out)
        report.add_artifact(os.getcwd(), p, 'clang-tidy')
    if errors_count + warn_count != 0:
        step.success = False
        url = format_url(
            "https://github.com/google/llvm-premerge-checks/blob/master/docs/clang_tidy.md"
            "#review-comments.", "why?")
        step.messages.append(
            f'clang-tidy found {errors_count} errors and {warn_count} warnings. {inline_comments} of them are added '
            f'as review comments {url}')
    logging.debug(f'report: {report}')
    logging.debug(f'step: {step}')