xref: /aosp_15_r20/external/crosvm/tools/contrib/cargo_refactor.py (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1# Copyright 2021 The ChromiumOS Authors
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5# Refactoring tools for moving around crates and updating dependencies
6# in toml files.
7#
8# Contains the last run refactoring for reference. Don't run this script, it'll
9# fail, but use it as a foundation for other refactorings.
10
11from contextlib import contextmanager
12from pathlib import Path
13import os
14import re
15import shutil
16import subprocess
17from typing import Callable, List, Tuple, Union
18
19
20SearchPattern = Union[str, re.Pattern[str]]
21Replacement = Union[str, Callable[[re.Match[str]], str]]
22
23
24def append_to_file(file_path: Path, appendix: str):
25    contents = file_path.read_text()
26    file_path.write_text(contents.rstrip() + "\n" + appendix + "\n")
27
28
29def replace_in_file(file_path: Path, search: SearchPattern, replace: Replacement):
30    if not file_path.exists():
31        print(f"WARNING: Does not exist {file_path}")
32        return
33    if isinstance(search, str):
34        search = re.escape(search)
35    contents = file_path.read_text()
36    (contents, count) = re.subn(search, replace, contents)
37    if count > 0:
38        print(f"replacing '{search}' with '{replace}' in {file_path}")
39        file_path.write_text(contents)
40
41
42def replace_in_files(glob: str, replacements: List[Tuple[SearchPattern, Replacement]]):
43    for file in Path().glob(glob):
44        for search, replace in replacements:
45            replace_in_file(file, search, replace)
46
47
48def replace_path_in_all_cargo_toml(old_path: Path, new_path: Path):
49    "Replace path in all cargo.toml files, accounting for relative paths."
50    for toml in Path().glob("**/Cargo.toml"):
51        crate_dir = toml.parent
52        old_rel = os.path.relpath(old_path, crate_dir)
53        new_rel = os.path.relpath(new_path, crate_dir)
54        replace_in_file(toml, re.escape(f'path = "{old_rel}"'), f'path = "{new_rel}"')
55
56
57def update_path_deps(toml: Path, from_path: Path, to_path: Path):
58    "Update path deps in toml file after moving it"
59    contents = toml.read_text()
60    for old_dep in re.findall('{ path = "([^"]+)"', contents):
61        new_dep = os.path.relpath((from_path / old_dep).resolve(), to_path)
62        contents = contents.replace(f'path = "{old_dep}"', f'path = "{new_dep}"')
63    toml.write_text(contents)
64
65
66def move_crate(from_path: Path, to_path: Path):
67    "Move crate and update dependencies"
68    print(f"{from_path} -> {to_path}")
69    if to_path.exists():
70        shutil.rmtree(to_path)
71    shutil.copytree(str(from_path), str(to_path))
72    update_path_deps(to_path / "Cargo.toml", from_path, to_path)
73    replace_in_files("**/*/Cargo.toml", [(str(from_path), str(to_path))])
74    replace_in_file(Path("Cargo.toml"), str(from_path), str(to_path))
75
76
77def update_workspace_members():
78    members: list[str] = []
79    members.append("members = [")
80    for toml in sorted(Path().glob("*/Cargo.toml")):
81        members.append(f'    "{toml.parent}",')
82    for toml in sorted(Path().glob("common/*/Cargo.toml")):
83        members.append(f'    "{toml.parent}",')
84    members.append('    "third_party/vmm_vhost",')
85
86    members.append("]")
87    replace_in_file(Path("Cargo.toml"), re.compile(r"members = \[[^\]]+\]"), "\n".join(members))
88
89
90@contextmanager
91def chdir(path: Union[Path, str]):
92    origin = Path().absolute()
93    try:
94        os.chdir(path)
95        yield
96    finally:
97        os.chdir(origin)
98
99
100def copy_crate_src_to_module(source: str, destination: str):
101    shutil.rmtree(destination, ignore_errors=True)
102    shutil.copytree(source, destination)
103    with chdir(destination):
104        Path("lib.rs").rename("mod.rs")
105
106
107IMPORT = """pub mod linux;
108
109#[cfg(windows)]
110pub mod windows;
111"""
112
113BUILD_RS = """\
114// Copyright 2022 The ChromiumOS Authors
115// Use of this source code is governed by a BSD-style license that can be
116// found in the LICENSE file.
117
118fn main() {
119    cc::Build::new()
120        .file("src/windows/stdio_fileno.c")
121        .compile("stdio_fileno");
122}
123"""
124
125
126def main():
127    os.chdir(Path(__file__).parent.parent.parent)
128    update_workspace_members()
129
130
131main()
132