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