Ejemplo n.º 1
0
def generate_fixes():
    """Generate compilation fixing patches"""
    unfixed_sources = Path(WORK_DIR, "unfixed/net/minecraft/server")
    unmapped_sources = Path(WORK_DIR, "unmapped/net/minecraft/server")
    fixes = Path(ROOT_DIR, "buildData/fixes")
    fixes.mkdir(exist_ok=True)
    existing_fixes = list(fixes.iterdir())
    if existing_fixes:
        print("Removing existing fixes")
        for p in existing_fixes:
            os.remove(p)
    engine = DiffEngine.create()
    for unfixed_file in unfixed_sources.iterdir():
        unmapped_file = Path(unmapped_sources, unfixed_file.name)
        original_lines = read_file(unfixed_file)
        fixed_lines = read_file(unmapped_file)
        patch = engine.diff(original_lines, fixed_lines)
        patch_lines = list(
            generate_unified_diff(str(unfixed_file.relative_to(ROOT_DIR)),
                                  str(unmapped_file.relative_to(ROOT_DIR)),
                                  original_lines, patch))
        if patch_lines:
            print(f"Found diff for {unfixed_file.name}")
            fix_file = Path(fixes, unfixed_file.name + ".patch")
            write_file(fix_file, patch_lines)
Ejemplo n.º 2
0
def do_diff(
    engine: DiffEngine,
    original: Path,
    revised: Path,
    output: Path,
    context_size=5,
    force=False,
):
    original_lines = []
    revised_lines = []
    with open(original, "rt") as f:
        for line in f:
            original_lines.append(line.rstrip("\r\n"))
    with open(revised, "rt") as f:
        for line in f:
            revised_lines.append(line.rstrip("\r\n"))
    result = engine.diff(original_lines, revised_lines)
    if not original.is_absolute():
        original_name = str(original)
    else:
        original_name = str(original.relative_to(Path.cwd()))
    if not revised.is_absolute():
        revised_name = str(revised)
    else:
        revised_name = str(revised.relative_to(Path.cwd()))
    try:
        result_lines = []
        empty = True
        for line in generate_unified_diff(
                original_name,
                revised_name,
                original_lines,
                result,
                context_size=context_size,
        ):
            if empty and line.strip():
                empty = False
            result_lines.append(line)
        if empty:
            return False
        with open(output, "wt" if force else "xt") as f:
            for line in result_lines:
                f.write(line)
                f.write("\n")
        return True
    except FileExistsError:
        raise CommandError("Output file already exists: {}".format(output))
Ejemplo n.º 3
0
def test_plain():
    do_test_engine(DiffEngine.create(name="plain"))
Ejemplo n.º 4
0
def test_native():
    do_test_engine(DiffEngine.create(name="native"))
Ejemplo n.º 5
0
def diff(original: Path,
         revised: Path,
         output: Path,
         ignore_missing=False,
         implementation=None,
         context=5,
         unrestricted=False,
         force=False):
    """Compute the difference between the original and revised text"""
    if not original.exists():
        raise CommandError("Original file doesn't exist: {}".format(original))
    if not revised.exists():
        raise CommandError("Revised file doesn't exist: {}".format(revised))
    try:
        engine = DiffEngine.create(name=implementation)
    except ImportError as e:
        raise CommandError("Unable to import {} implementation!".format(
            implementation)) from e
    if original.is_dir():
        if not revised.is_dir():
            raise CommandError(
                "Original {} is a directory, but revised {} is a file!".format(
                    original, revised))
        for revised_root, dirs, files in os.walk(str(revised)):
            for revised_file_name in files:
                if not unrestricted and revised_file_name.startswith('.'):
                    continue
                revised_file = Path(revised_root, revised_file_name)
                relative_path = revised_file.relative_to(revised)
                original_file = Path(original, relative_path)
                if not original_file.exists():
                    if ignore_missing:
                        continue
                    else:
                        raise CommandError(
                            "Revised file {} doesn't have matching original {}!"
                            .format(revised_file, original_file))
                output_file = Path(output, relative_path.parent,
                                   relative_path.name + ".patch")
                output_file.parent.mkdir(parents=True, exist_ok=True)
                if do_diff(engine,
                           original_file,
                           revised_file,
                           output_file,
                           context_size=context,
                           force=force):
                    print("Computed diff: {}".format(relative_path))
            if not unrestricted:
                hidden_dirs = [d for d in dirs if d.startswith('.')]
                for d in hidden_dirs:
                    dirs.remove(d)
    else:
        if not revised.is_file():
            raise CommandError(
                "Original {} is a file, but revised {} is a directory!".format(
                    original, revised))
        do_diff(engine,
                original,
                revised,
                output,
                context_size=context,
                force=force)
Ejemplo n.º 6
0
def diff(quiet=False, context=5, implementation=None):
    """Regenerates the patch files from the contents of the working directory."""
    unpatched_sources = Path(WORK_DIR, "unpatched")
    if not unpatched_sources.exists():
        raise CommandError("Couldn't find unpatched sources!")
    patched_dir = Path(Path.cwd(), "patched")
    if not patched_dir.exists():
        raise CommandError("No patched files found!")
    patches = Path(Path.cwd(), "patches")
    patches.mkdir(exist_ok=True)
    if implementation is not None:
        try:
            engine = DiffEngine.create(implementation)
            print(f"Using {repr(engine)} diff implementation.")
        except ImportError as e:
            raise CommandError(
                f"Unable to import {implementation} engine: {e}")
    else:
        try:
            engine = DiffEngine.create('native')
        except ImportError:
            print("WARNING: Unable to import native diff implementation",
                  file=stderr)
            print("Calculating diffs will be over 10 times slower!",
                  file=stderr)
            engine = DiffEngine.create('plain')
    print("---- Recomputing Fountain patches via DiffUtils")
    for revised_root, dirs, files in os.walk(str(patched_dir)):
        for revised_file_name in files:
            if revised_file_name.startswith('.'):
                continue  # Ignore dotfiles
            revised_file = Path(revised_root, revised_file_name)
            relative_path = revised_file.relative_to(patched_dir)
            original_file = Path(unpatched_sources, relative_path)
            if not original_file.exists():
                raise CommandError(
                    f"Revised file {revised_file} doesn't have matching original!"
                )
            patch_file = Path(patches, relative_path.parent,
                              relative_path.name + ".patch")
            patch_file.parent.mkdir(parents=True, exist_ok=True)
            original_lines = []
            revised_lines = []
            with open(original_file, 'rt') as f:
                for line in f:
                    original_lines.append(line.rstrip('\r\n'))
            with open(revised_file, 'rt') as f:
                for line in f:
                    revised_lines.append(line.rstrip('\r\n'))
            result = engine.diff(original_lines, revised_lines)
            original_name = str(original_file.absolute().relative_to(ROOT_DIR))
            revised_name = str(revised_file.absolute().relative_to(ROOT_DIR))
            result_lines = []
            empty = True
            for line in generate_unified_diff(original_name,
                                              revised_name,
                                              original_lines,
                                              result,
                                              context_size=context):
                if empty and line.strip():
                    empty = False
                result_lines.append(line)
            if empty:
                continue
            elif not quiet:
                print(f"Found diff for {relative_path}")
            with open(patch_file, 'wt') as f:
                for line in result_lines:
                    f.write(line)
                    f.write('\n')
        # Strip hidden dotfile dirs
        hidden_dirs = [d for d in dirs if d.startswith('.')]
        for d in hidden_dirs:
            dirs.remove(d)
Ejemplo n.º 7
0
def test_plain():
    do_test_engine(DiffEngine.create(name='plain'))
Ejemplo n.º 8
0
def test_native():
    do_test_engine(DiffEngine.create(name='native'))
Ejemplo n.º 9
0
def main():
    parser = ArgumentParser(description="Benchmarks DiffUtils")
    available_targets = frozenset(bench_methods.keys())
    parser.add_argument(
        "targets",
        nargs="+",
        choices=[*available_targets, "all"],
        help="The items to benchmark",
    )
    parser.add_argument(
        "--iterations",
        "-i",
        default=10,
        help="The number of benchmark iterations to perform on each",
    )
    parser.add_argument("--repeat",
                        "-r",
                        default=3,
                        help="The number of times to repeat the benchmark")
    parser.add_argument(
        "--data-dir",
        dest="data_dir",
        default="{}/data".format(dirname(__file__)),
        help="The location of the benchmarking data",
    )
    args = parser.parse_args()
    iterations = args.iterations
    repeat = args.repeat
    targets = args.targets
    data_dir = args.data_dir
    if "all" in targets:
        if len(targets) > 1:
            print(
                "When the 'all' target is specfied, no other targets may be specified",
                file=stderr,
            )
            exit(1)
        targets = available_targets
    max_target_length = max(len(target) for target in targets)
    for target in sorted(targets):
        padded_target = target.ljust(max_target_length)
        for (original_name, revised_name) in test_data:
            original_lines = test_data_lines(original_name, data_dir=data_dir)
            revised_lines = test_data_lines(revised_name, data_dir=data_dir)
            setup_code, bench_code = bench_methods[target]
            setup_code = textwrap.dedent(setup_code)
            bench_code = textwrap.dedent(bench_code)
            if target == "diff":
                engines = DiffEngine.available_engines()
            else:
                engines = None
            bench_env = dict(globals())
            for local_param in (
                    "original_name",
                    "revised_name",
                    "original_lines",
                    "revised_lines",
            ):
                existing_value = bench_env.get(local_param)
                if existing_value is not None:
                    raise AssertionError("{} already present: {}".format(
                        local_param, repr(existing_value)))
                bench_env[local_param] = locals()[local_param]

            def run_bench(bench_env, engine_name=None):
                timer = Timer(setup=setup_code,
                              stmt=bench_code,
                              globals=bench_env)
                try:
                    result = min(timer.repeat(repeat=repeat,
                                              number=iterations))
                except Exception:
                    message = "Unable to run {} between {} and {}".format(
                        target, original_name, revised_name)
                    if engine_name:
                        message += " with {}".format(engine_name)
                    print(message, file=stderr)
                    timer.print_exc()
                    exit(1)
                result *= 1000
                message = "{}  {:.3f} ms -- {} and {}".format(
                    padded_target, result, original_name, revised_name)
                if engine_name:
                    message += " with {}".format(engine_name)
                print(message)

            assert "engine" not in bench_env, "engine already present: " + repr(
                bench_env["engine"])
            if engines is not None:
                for engine in DiffEngine.available_engines():
                    bench_env["engine"] = engine
                    run_bench(bench_env, engine_name=repr(engine))
            else:
                run_bench(bench_env)