예제 #1
0
def test_config():
    config_file = Config(os.path.abspath(os.getcwd()), ".styleguide")
    assert config_file.is_modifiable_file("." + os.sep + "wpiformat" + os.sep +
                                          "javaguidelink.png")
    assert config_file.is_generated_file("." + os.sep + "wpiformat" + os.sep +
                                         "wpiformat" + os.sep + "cpplint.py")

    assert not config_file.is_generated_file("." + os.sep + "wpiformat" +
                                             os.sep + "diff_cpplint.py")
    assert not config_file.is_generated_file("." + os.sep + "wpiformat" +
                                             os.sep + "update_cpplint.py")
    assert not config_file.is_modifiable_file("." + os.sep + "wpiformat" +
                                              os.sep + "license.txt")
예제 #2
0
def main():
    # Parse command-line arguments
    parser = argparse.ArgumentParser(
        description=
        "Runs all formatting tasks on the code base. This should be invoked from a directory within the project."
    )
    parser.add_argument(
        "-v",
        dest="verbose1",
        action="store_true",
        help="verbosity level 1 (prints names of processed files)")
    parser.add_argument(
        "-vv",
        dest="verbose2",
        action="store_true",
        help=
        "verbosity level 2 (prints names of processed files and tasks run on them)"
    )
    parser.add_argument(
        "-j",
        dest="jobs",
        type=int,
        default=mp.cpu_count(),
        help="number of jobs to run (default is number of cores)")
    parser.add_argument(
        "-y",
        dest="year",
        type=int,
        default=date.today().year,
        help=
        "year to use when updating license headers (default is current year)")
    parser.add_argument(
        "-clang",
        dest="clang_version",
        type=str,
        default="",
        help=
        "version suffix for clang-format (invokes \"clang-format-CLANG_VERSION\" or \"clang-format\" if no suffix provided)"
    )
    parser.add_argument(
        "-f",
        dest="file",
        type=str,
        default="",
        nargs="+",
        help=
        "file or directory names (can be path relative to python invocation directory or absolute path)"
    )
    args = parser.parse_args()

    # All discovered files are relative to Git repo root directory, so find the
    # root.
    root_path = Task.get_repo_root()
    if root_path == "":
        print("Error: not invoked within a Git repository", file=sys.stderr)
        sys.exit(1)

    # If no files explicitly specified
    if not args.file:
        # Delete temporary files from previous incomplete run
        files = [
            os.path.join(dp, f)
            for dp, dn, fn in os.walk(root_path)
            for f in fn
            if f.endswith(".tmp")
        ]
        for f in files:
            os.remove(f)

        # Recursively create list of files in given directory
        files = [
            os.path.join(dp, f) for dp, dn, fn in os.walk(root_path) for f in fn
        ]

        if not files:
            print("Error: no files found to format", file=sys.stderr)
            sys.exit(1)
    else:
        files = []
        for name in args.file:
            # If a directory was specified, recursively expand it
            if os.path.isdir(name):
                files.extend([
                    os.path.join(dp, f)
                    for dp, dn, fn in os.walk(name)
                    for f in fn
                ])
            else:
                files.append(name)

    # Convert relative paths of files to absolute paths
    files = [os.path.abspath(name) for name in files]

    # Don't run tasks on Git metadata
    files = [name for name in files if os.sep + ".git" + os.sep not in name]

    # Don't check for changes in or run tasks on ignored files
    files = filter_ignored_files(files)

    # Create list of all changed files
    changed_file_list = []

    output_list = subprocess.run(
        ["git", "diff", "--name-only", "master"],
        stdout=subprocess.PIPE).stdout.split()
    for line in output_list:
        changed_file_list.append(root_path + os.sep +
                                 line.strip().decode("ascii"))

    # Don't run tasks on modifiable or generated files
    work = []
    for name in files:
        config_file = Config(os.path.dirname(name), ".styleguide")

        if config_file.is_modifiable_file(name):
            continue
        if config_file.is_generated_file(name):
            # Emit warning if a generated file was editted
            if name in changed_file_list:
                print("Warning: generated file '" + name + "' modified")
            continue

        work.append(name)
    files = work

    # If there are no files left, do nothing
    if len(files) == 0:
        sys.exit(0)

    # Prepare file batches for batch tasks
    chunksize = math.ceil(len(files) / args.jobs)
    file_batches = [
        files[i:i + chunksize] for i in range(0, len(files), chunksize)
    ]

    # IncludeOrder is run after Stdlib so any C std headers changed to C++ or
    # vice versa are sorted properly. ClangFormat is run after the other tasks
    # so it can clean up their formatting.
    task_pipeline = [
        BraceComment(),
        CIdentList(),
        IncludeGuard(),
        LicenseUpdate(str(args.year)),
        JavaClass(),
        Newline(),
        Stdlib(),
        IncludeOrder(),
        UsingDeclaration(),
        UsingNamespaceStd(),
        Whitespace()
    ]
    run_pipeline(task_pipeline, args, files)

    task_pipeline = [ClangFormat(args.clang_version)]
    run_batch(task_pipeline, args, file_batches)

    # These tasks fix clang-format formatting
    task_pipeline = [Jni()]
    run_pipeline(task_pipeline, args, files)

    # Lint is run last since previous tasks can affect its output.
    task_pipeline = [PyFormat(), Lint()]
    run_batch(task_pipeline, args, file_batches)