Exemplo n.º 1
0
def analyze_file(rst_file):

    analysis_error = False

    with open(rst_file) as f:
        content = f.read()

    blocks = list(
        enumerate(
            filter(
                lambda b: b.language in ["ada", "c"]
                if isinstance(b, CodeBlock) else True,
                Block.get_blocks(content))))

    code_blocks = [(i, b) for i, b in blocks if isinstance(b, CodeBlock)]

    def run(*run_args):
        if args.verbose:
            print("Running \"{}\"".format(" ".join(run_args)))
        try:
            output = S.check_output(run_args, stderr=S.STDOUT).decode("utf-8")
            all_output.extend(output.splitlines())
        except S.CalledProcessError as e:
            all_output.extend(e.output.decode("utf-8").splitlines())
            raise e

        return output

    if args.code_block_at:
        for i, block in code_blocks:
            block.run = False
            if block.line_start < args.code_block_at < block.line_end:
                block.run = True

    if args.code_block:
        expr = "code_blocks[{}]".format(args.code_block)
        subset = eval(expr, globals(), locals())
        if not isinstance(code_blocks, list):
            subset = [subset]

        for i, code_block in code_blocks:
            code_block.run = False

        for i, code_block in subset:
            code_block.run = True

    def extract_diagnostics(lines):
        diags = []
        r = re.compile("(.+?):(\d+):(\d+): (.+)")
        for l in lines:
            m = r.match(l)
            if m:
                f, l, c, t = m.groups()
                diags.append(Diag(f, int(l), int(c), t))
        return diags

    def remove_string(some_text, rem):
        return re.sub(".*" + rem + ".*\n?", "", some_text)

    projects = dict()

    for (i, b) in code_blocks:
        if b.project is None:
            print("Error: project not set in {} at line {}".format(
                rst_file, str(b.line_start)))
            exit(1)

        if not b.project in projects:
            projects[b.project] = list()
        projects[b.project].append((i, b))

    work_dir = os.getcwd()
    base_project_dir = "projects"

    for project in projects:

        def init_project_dir(project):
            project_dir = base_project_dir + "/" + project.replace(".", "/")

            if os.path.exists(project_dir):
                shutil.rmtree(project_dir)

            try:
                os.makedirs(project_dir)
            except OSError as e:
                if e.errno != errno.EEXIST:
                    raise

            os.chdir(project_dir)

        init_project_dir(project)

        if args.verbose:
            print(header("Checking project {}".format(project)))
            print("Number of code blocks: {}".format(len(projects[project])))

        for i, block in projects[project]:
            if isinstance(block, ConfigBlock):
                current_config.update(block)
                continue

            has_error = False
            loc = "at {}:{} (code block #{})".format(rst_file,
                                                     block.line_start, i)

            all_output = []

            def print_diags():
                diags = extract_diagnostics(all_output)
                for diag in diags:
                    diag.line = diag.line + block.line_start
                    diag.file = rst_file
                    print(diag)

            def print_error(*error_args):
                error(*error_args)
                print_diags()

            no_check = any(sphinx_class in ["ada-nocheck", "c-nocheck"]
                           for sphinx_class in block.classes)
            if no_check:
                if args.verbose:
                    print("Skipping code block {}".format(loc))
                continue

            if args.verbose:
                print(header("Checking code block {}".format(loc)))

            split = block.text.splitlines()

            source_files = list()
            if block.manual_chop:
                source_files = manual_chop(split)
            else:
                source_files = real_gnatchop(split)

            if len(source_files) == 0:
                print_error(loc, "Failed to chop example, skipping\n")
                analysis_error = True
                continue

            for source_file in source_files:

                with open(source_file.basename, u"w") as code_file:
                    code_file.write(source_file.content)

                try:
                    if block.language == "ada":
                        out = run("gcc", "-c", "-gnats", "-gnatyg0-s",
                                  source_file.basename)
                    elif block.language == "c":
                        out = run("gcc", "-c", source_file.basename)

                    if out:
                        print_error(loc, "Failed to syntax check example")
                        has_error = True
                except S.CalledProcessError:
                    print_error(loc, "Failed to syntax check example")
                    has_error = True

            if 'ada-syntax-only' in block.classes or not block.run:
                continue

            compile_error = False
            prove_error = False
            is_prove_error_class = False

            prove_buttons = [
                "prove", "prove_flow", "prove_flow_report_all",
                "prove_report_all"
            ]

            def get_main_filename(block):
                if block.main_file is not None:
                    main_file = block.main_file
                else:
                    main_file = source_files[-1].basename
                return main_file

            def make_project_block_dir():
                project_block_dir = str(block.line_start)
                if not os.path.exists(project_block_dir):
                    os.makedirs(project_block_dir)

                return project_block_dir

            if (('ada-run' in block.classes or 'ada-run-expect-failure'
                 in block.classes or 'run' in block.buttons)
                    and not 'ada-norun' in block.classes):
                main_file = get_main_filename(block)

                project_block_dir = make_project_block_dir()

                if block.language == "ada":

                    try:
                        out = run("gprbuild", "-gnata", "-gnatyg0-s", "-f",
                                  main_file)
                    except S.CalledProcessError as e:
                        if 'ada-expect-compile-error' in block.classes:
                            compile_error = True
                        else:
                            print_error(loc, "Failed to compile example")
                            print(e.output)
                            has_error = True
                        out = str(e.output.decode("utf-8"))

                    out = remove_string(out, "using project")
                    with open(project_block_dir + "/build.log",
                              u"w") as logfile:
                        logfile.write(out)

                elif block.language == "c":
                    try:
                        cmd = ["gcc", "-o",
                               P.splitext(main_file)[0]] + glob.glob('*.c')
                        out = run(*cmd)
                    except S.CalledProcessError as e:
                        if 'c-expect-compile-error' in block.classes:
                            compile_error = True
                        else:
                            print_error(loc, "Failed to compile example")
                            print(e.output)
                            has_error = True
                        out = str(e.output.decode("utf-8"))
                    with open(project_block_dir + "/build.log",
                              u"w") as logfile:
                        logfile.write(out)

                if not compile_error and not has_error:
                    if block.language == "ada":
                        try:
                            out = run("./{}".format(P.splitext(main_file)[0]))

                            if 'ada-run-expect-failure' in block.classes:
                                print_error(
                                    loc,
                                    "Running of example should have failed")
                                has_error = True

                        except S.CalledProcessError as e:
                            if 'ada-run-expect-failure' in block.classes:
                                if args.verbose:
                                    print(
                                        "Running of example expectedly failed")
                            else:
                                print_error(loc, "Running of example failed")
                                has_error = True

                            out = str(e.output.decode("utf-8"))

                        with open(project_block_dir + "/run.log",
                                  u"w") as logfile:
                            logfile.write(out)

                    elif block.language == "c":
                        try:
                            out = run("./{}".format(P.splitext(main_file)[0]))

                            if 'c-run-expect-failure' in block.classes:
                                print_error(
                                    loc,
                                    "Running of example should have failed")
                                has_error = True

                        except S.CalledProcessError as e:
                            if 'c-run-expect-failure' in block.classes:
                                if args.verbose:
                                    print(
                                        "Running of example expectedly failed")
                            else:
                                print_error(loc, "Running of example failed")
                                has_error = True
                            out = str(e.output.decode("utf-8"))

                        with open(project_block_dir + "/run.log",
                                  u"w") as logfile:
                            logfile.write(out)

            if 'compile' in block.buttons:

                project_block_dir = make_project_block_dir()

                for source_file in source_files:
                    if block.language == "ada":
                        try:
                            out = run("gcc", "-c", "-gnatc", "-gnatyg0-s",
                                      source_file.basename)
                        except S.CalledProcessError as e:
                            if 'ada-expect-compile-error' in block.classes:
                                compile_error = True
                            else:
                                print_error(loc, "Failed to compile example")
                                has_error = True
                            out = str(e.output.decode("utf-8"))

                        with open(project_block_dir + "/compile.log",
                                  u"w+") as logfile:
                            logfile.write(out)

                    elif block.language == "c":
                        try:
                            out = run("gcc", "-c", source_file.basename)
                        except S.CalledProcessError as e:
                            if 'c-expect-compile-error' in block.classes:
                                compile_error = True
                            else:
                                print_error(loc, "Failed to compile example")
                                has_error = True
                            out = str(e.output.decode("utf-8"))

                        with open(project_block_dir + "/compile.log",
                                  u"w+") as logfile:
                            logfile.write(out)

            if any(b in prove_buttons for b in block.buttons):

                if block.language == "ada":
                    project_block_dir = make_project_block_dir()

                    main_file = get_main_filename(block)
                    spark_mode = True
                    project_filename = write_project_file(
                        main_file, block.compiler_switches, spark_mode)

                    is_prove_error_class = any(c in [
                        'ada-expect-prove-error', 'ada-expect-compile-error',
                        'ada-run-expect-failure'
                    ] for c in block.classes)
                    extra_args = []

                    if 'prove_flow' in block.buttons:
                        extra_args = ["--mode=flow"]
                    elif 'prove_flow_report_all' in block.buttons:
                        extra_args = ["--mode=flow", "--report=all"]
                    elif 'prove_report_all' in block.buttons:
                        extra_args = ["--report=all"]

                    line = [
                        "gnatprove", "-P", project_filename,
                        "--checks-as-errors", "--level=0", "--no-axiom-guard"
                    ]
                    line.extend(extra_args)

                    try:
                        out = run(*line)
                    except S.CalledProcessError as e:
                        if is_prove_error_class:
                            prove_error = True
                        else:
                            print_error(loc, "Failed to prove example")
                            print(e.output)
                            has_error = True
                        out = str(e.output.decode("utf-8"))

                    out = remove_string(out, "Summary logged in")
                    with open(project_block_dir + "/prove.log",
                              u"w") as logfile:
                        logfile.write(out)
                else:
                    print_error(loc,
                                "Wrong language selected for prove button")
                    print(e.output)
                    has_error = True

            if len(block.buttons) == 0:
                print_error(
                    loc, "Expected at least 'no_button' indicator, got none!")
                has_error = True

            if 'ada-expect-compile-error' in block.classes:
                if not any(b in ['compile', 'run'] for b in block.buttons):
                    print_error(loc,
                                "Expected compile or run button, got none!")
                    has_error = True
                if not compile_error:
                    print_error(loc, "Expected compile error, got none!")
                    has_error = True

            if 'ada-expect-prove-error' in block.classes:
                if not any(b in prove_buttons for b in block.buttons):
                    print_error(loc, "Expected prove button, got none!")
                    has_error = True

            if any(b in prove_buttons for b in block.buttons):
                if is_prove_error_class and not prove_error:
                    print_error(loc, "Expected prove error, got none!")
                    has_error = True

            if (any(c in ['ada-run-expect-failure', 'ada-norun']
                    for c in block.classes) and not 'run' in block.buttons):
                print_error(loc, "Expected run button, got none!")
                has_error = True

            if has_error:
                analysis_error = True
            elif args.verbose:
                print(C.col("SUCCESS", C.Colors.GREEN))

            if args.all_diagnostics:
                print_diags()

        os.chdir(work_dir)

    if os.path.exists(base_project_dir) and not args.keep_files:
        shutil.rmtree(base_project_dir)

    return analysis_error
Exemplo n.º 2
0
    def get_blocks(input_text):
        lang_re = re.compile("\s*.. code::\s*(\w+)?\s*")
        project_re = re.compile("\s*.. code::.*project=(\S+)?")
        main_re = re.compile("\s*.. code::.*main=(\S+)?")
        manual_chop_re = re.compile("\s*.. code::.*manual_chop?")
        button_re = re.compile("\s+(\S+)_button")
        code_config_re = re.compile(":code-config:`(.*)?`")
        classes_re = re.compile("\s*:class:\s*(.+)")
        switches_re = re.compile("\s*.. code::.*switches=(\S+)?")
        compiler_switches_re = re.compile("Compiler[(](\S+)?[)]")

        blocks = []
        lines = input_text.splitlines()

        def first_nonws(line):
            for i, c in enumerate(line):
                if not c.isspace():
                    return i
            return 0

        indents = map(first_nonws, lines)

        classes = []
        compiler_switches = []
        buttons = []
        cb_start = -1
        cb_indent = -1
        lang = ""
        project = None
        main_file = None
        manual_chop = None
        last_line_number = -1

        def is_empty(line):
            return (not line) or line.isspace()

        def process_block(i, line, indent):
            nonlocal classes, cb_start, cb_indent, lang

            if cb_indent == -1 and not is_empty(line):
                cb_indent = indent

            if indent < cb_indent and not is_empty(line):
                blocks.append(
                    CodeBlock(
                        cb_start, i, "\n".join(l[cb_indent:]
                                               for l in lines[cb_start:i]),
                        lang, project, main_file, compiler_switches, classes,
                        manual_chop, buttons))

                classes, cb_start, cb_indent, lang = [], -1, -1, ""

            m = classes_re.match(line)

            if m:
                classes = [str.strip(l) for l in m.groups()[0].split(",")]
                cb_start = i + 1

        def start_code_block(i, line, indent):
            nonlocal cb_start, lang, project, main_file, manual_chop, \
                     buttons, compiler_switches

            cb_start, lang = (i + 1, lang_re.match(line).groups()[0])
            project = project_re.match(line)
            if project is not None:
                project = project.groups()[0]

            main_file = main_re.match(line)
            if main_file is not None:
                # Retrieve actual main filename
                main_file = main_file.groups()[0]
            if lang == "c":
                manual_chop = True
            else:
                manual_chop = (manual_chop_re.match(line) is not None)
            buttons = button_re.findall(line)

            all_switches = switches_re.match(line)

            compiler_switches = []
            if all_switches is not None:
                all_switches = all_switches.groups()[0]
                compiler_switches = compiler_switches_re.match(all_switches)
                if compiler_switches is not None:
                    compiler_switches = [
                        str.strip(l)
                        for l in compiler_switches.groups()[0].split(",")
                    ]

        def start_config_block(i, line, indent):
            blocks.append(
                ConfigBlock(**dict(
                    kv.split('=')
                    for kv in code_config_re.findall(line)[0].split(";"))))

        for i, (line, indent) in enumerate(zip(lines, indents)):
            last_line_number = i

            if cb_start != -1:
                process_block(i, line, indent)
            else:
                if line[indent:].startswith(".. code::"):
                    start_code_block(i, line, indent)
                elif line[indent:].startswith(":code-config:"):
                    start_config_block(i, line, indent)

        if cb_start != -1:
            print(
                "{}: code block (start: {}, project: {}) doesn't have explanatory section!"
                .format(C.col("WARNING", C.Colors.YELLOW), cb_start, project))
            process_block(last_line_number + 1, "END", 0)

            # Error: unable to process last code block
            if cb_start != -1:
                print(
                    "{}: code block (start: {}, project: {}) hasn't been successfully processed!"
                    .format(C.col("ERROR", C.Colors.RED), cb_start, project))
                exit(1)

        return blocks
Exemplo n.º 3
0
def error(loc, strn):
    print("{} {}: {}".format(C.col("ERROR", C.Colors.RED), loc, strn))
Exemplo n.º 4
0
def header(strn):
    return C.col("{}\n{}\n".format(strn, '*' * len(strn)), C.Colors.BLUE)
Exemplo n.º 5
0
    if os.path.exists(base_project_dir) and not args.keep_files:
        shutil.rmtree(base_project_dir)

    return analysis_error


# Remove the build dir, but only if the user didn't ask for a specific
# subset of code_blocks
if (os.path.exists(args.build_dir) and not args.code_block
        and not args.keep_files):
    shutil.rmtree(args.build_dir)

if not os.path.exists(args.build_dir):
    os.makedirs(args.build_dir)

os.chdir(args.build_dir)

test_error = False

for f in args.rst_files:
    analysis_error = analyze_file(f)
    if analysis_error:
        test_error = True

if test_error:
    print(C.col("TEST ERROR", C.Colors.RED))
    exit(1)
elif args.verbose:
    print(C.col("TEST SUCCESS", C.Colors.GREEN))
Exemplo n.º 6
0
def analyze_file(rst_file):

    with open(rst_file) as f:
        content = f.read()

    blocks = list(
        enumerate(
            filter(
                lambda b: b.language == "ada"
                if isinstance(b, CodeBlock) else True,
                Block.get_blocks(content))))

    code_blocks = [(i, b) for i, b in blocks if isinstance(b, CodeBlock)]

    def run(*run_args):
        if args.verbose:
            print "Running \"{}\"".format(" ".join(run_args))
        try:
            output = S.check_output(run_args, stderr=S.STDOUT)
            all_output.extend(output.splitlines())
        except S.CalledProcessError as e:
            all_output.extend(e.output.splitlines())
            raise e

        return output

    if args.code_block_at:
        for i, block in code_blocks:
            block.run = False
            if block.line_start < args.code_block_at < block.line_end:
                block.run = True

    if args.code_block:
        expr = "code_blocks[{}]".format(args.code_block)
        subset = eval(expr, globals(), locals())
        if not isinstance(code_blocks, list):
            subset = [subset]

        for i, code_block in code_blocks:
            code_block.run = False

        for i, code_block in subset:
            code_block.run = True

    def extract_diagnostics(lines):
        diags = []
        r = re.compile("(.+?):(\d+):(\d+): (.+)")
        for l in lines:
            m = r.match(l)
            if m:
                f, l, c, t = m.groups()
                diags.append(Diag(f, int(l), int(c), t))
        return diags

    for i, block in blocks:
        if isinstance(block, ConfigBlock):
            current_config.update(block)
            continue

        has_error = False
        loc = "at {}:{} (code block #{})".format(rst_file, block.line_start, i)

        all_output = []

        def print_diags():
            diags = extract_diagnostics(all_output)
            for diag in diags:
                diag.line = diag.line + block.line_start
                diag.file = rst_file
                print diag

        def print_error(*error_args):
            error(*error_args)
            print_diags()

        if 'ada-nocheck' in block.classes:
            if args.verbose:
                print "Skipping code block {}".format(loc)
            continue

        if args.verbose:
            print header("Checking code block {}".format(loc))

        with open(u"code.ada", u"w") as code_file:
            code_file.write(block.text)

        try:
            out = run("gnatchop", "-r", "-w", "code.ada").splitlines()
        except S.CalledProcessError:
            print_error(loc, "Failed to chop example, skipping\n")
            continue

        idx = -1
        for i, line in enumerate(out):
            if line.endswith("into:"):
                idx = i + 1
                break

        if idx == -1:
            print_error(loc, "Failed to chop example, skipping\n")
            continue

        source_files = [s.strip() for s in out[idx:]]

        for source_file in source_files:
            try:
                out = run("gcc", "-c", "-gnats", "-gnatyg0-s", source_file)
            except S.CalledProcessError:
                print_error(loc, "Failed to syntax check example")
                has_error = True

            if out:
                print_error(loc, "Failed to syntax check example")
                has_error = True

        if 'ada-syntax-only' in block.classes or not block.run:
            continue

        compile_error = False

        if ('ada-run' in block.classes
                or 'ada-run-expect-failure' in block.classes):
            if len(source_files) == 1:
                main_file = source_files[0]
            else:
                main_file = 'main.adb'

            try:
                out = run("gprbuild", "-gnata", "-gnatyg0-s", "-f", main_file)
            except S.CalledProcessError as e:
                print_error(loc, "Failed to compile example")
                print e.output
                has_error = True

            if not has_error:
                try:
                    run("./{}".format(P.splitext(main_file)[0]))

                    if 'ada-run-expect-failure' in block.classes:
                        print_error(loc,
                                    "Running of example should have failed")
                        has_error = True

                except S.CalledProcessError:
                    if 'ada-run-expect-failure' in block.classes:
                        if args.verbose:
                            print "Running of example expectedly failed"
                    else:
                        print_error(loc, "Running of example failed")
                        has_error = True

        else:
            for source_file in source_files:
                try:
                    run("gcc", "-c", "-gnatc", "-gnatyg0-s", source_file)
                except S.CalledProcessError:
                    if 'ada-expect-compile-error' in block.classes:
                        compile_error = True
                    else:
                        print_error(loc, "Failed to compile example")
                        has_error = True

        if 'ada-expect-compile-error' in block.classes and not compile_error:
            print_error(loc, "Expected compile error, got none!")
            has_error = True

        if not has_error and args.verbose:
            print C.col("SUCCESS", C.Colors.GREEN)

        if args.all_diagnostics:
            print_diags()

        if source_files and not current_config.accumulate_code:
            for source_file in source_files:
                os.remove(source_file)
Exemplo n.º 7
0
                    has_error = True

            except S.CalledProcessError:
                if 'ada-run-expect-failure' in code_block.classes:
                    if args.verbose:
                        print "Running of example expectedly failed"
                else:
                    print_error(loc, "Running of example failed")
                    has_error = True

    else:
        for source_file in source_files:
            try:
                run("gcc", "-c", "-gnatc", "-gnaty", source_file)
            except S.CalledProcessError:
                if 'ada-expect-compile-error' in code_block.classes:
                    compile_error = True
                else:
                    print_error(loc, "Failed to compile example")
                    has_error = True

    if 'ada-expect-compile-error' in code_block.classes and not compile_error:
        print_error(loc, "Expected compile error, got none!")
        has_error = True

    if not has_error and args.verbose:
        print C.col("SUCCESS", C.Colors.GREEN)

    if args.all_diagnostics:
        print_diags()