migrate_cpp.py 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. """Migrates C++ code to Carbon."""
  2. __copyright__ = """
  3. Part of the Carbon Language project, under the Apache License v2.0 with LLVM
  4. Exceptions. See /LICENSE for license information.
  5. SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  6. """
  7. import argparse
  8. import glob
  9. import os
  10. import subprocess
  11. import sys
  12. from typing import Optional
  13. _CPP_REFACTORING = "./cpp_refactoring/cpp_refactoring"
  14. _H_EXTS = {".h", ".hpp"}
  15. _CPP_EXTS = {".c", ".cc", ".cpp", ".cxx"}
  16. class _Workflow:
  17. _parsed_args: argparse.Namespace
  18. _data_dir: str
  19. _cpp_files: Optional[list[str]]
  20. def __init__(self) -> None:
  21. """Parses command-line arguments and flags."""
  22. parser = argparse.ArgumentParser(description=__doc__)
  23. parser.add_argument(
  24. "dir",
  25. type=str,
  26. help="A directory containing C++ files to migrate to Carbon.",
  27. )
  28. parsed_args = parser.parse_args()
  29. self._parsed_args = parsed_args
  30. self._data_dir = os.path.dirname(sys.argv[0])
  31. # Validate arguments.
  32. if not os.path.isdir(parsed_args.dir):
  33. sys.exit("%r must point to a directory." % parsed_args.dir)
  34. def run(self) -> None:
  35. """Runs the migration workflow."""
  36. try:
  37. self._gather_files()
  38. self._clang_tidy()
  39. self._cpp_refactoring()
  40. self._rename_files()
  41. self._print_header("Done!")
  42. except subprocess.CalledProcessError as e:
  43. # Discard the stack for subprocess errors.
  44. sys.exit(str(e))
  45. def _data_file(self, relative_path: str) -> str:
  46. """Returns the path to a data file."""
  47. return os.path.join(self._data_dir, relative_path)
  48. @staticmethod
  49. def _print_header(header: str) -> None:
  50. print("*" * 79)
  51. print("* %-75s *" % header)
  52. print("*" * 79)
  53. def _gather_files(self) -> None:
  54. """Returns the list of C++ files to convert."""
  55. self._print_header("Gathering C++ files...")
  56. all_files = glob.glob(
  57. os.path.join(self._parsed_args.dir, "**/*.*"), recursive=True
  58. )
  59. exts = _CPP_EXTS.union(_H_EXTS)
  60. cpp_files = [f for f in all_files if os.path.splitext(f)[1] in exts]
  61. if not cpp_files:
  62. sys.exit(
  63. "%r doesn't contain any C++ files to convert."
  64. % self._parsed_args.dir
  65. )
  66. self._cpp_files = sorted(cpp_files)
  67. print("%d files found." % len(self._cpp_files))
  68. def _clang_tidy(self) -> None:
  69. """Runs clang-tidy to fix C++ files in a directory."""
  70. self._print_header("Running clang-tidy...")
  71. with open(self._data_file("clang_tidy.yaml")) as f:
  72. config = f.read()
  73. subprocess.run(
  74. ["run-clang-tidy.py", "-fix", "-config", config],
  75. check=True,
  76. )
  77. def _cpp_refactoring(self) -> None:
  78. """Runs cpp_refactoring to migrate C++ files towards Carbon syntax."""
  79. self._print_header("Running cpp_refactoring...")
  80. cpp_refactoring = self._data_file(_CPP_REFACTORING)
  81. assert self._cpp_files is not None
  82. subprocess.run([cpp_refactoring] + self._cpp_files, check=True)
  83. def _rename_files(self) -> None:
  84. """Renames C++ files to the destination Carbon filenames."""
  85. api_renames = 0
  86. impl_renames = 0
  87. assert self._cpp_files is not None
  88. for f in self._cpp_files:
  89. parts = os.path.splitext(f)
  90. if parts[1] in _H_EXTS:
  91. os.rename(f, parts[0] + ".carbon")
  92. api_renames += 1
  93. else:
  94. os.rename(f, parts[0] + ".impl.carbon")
  95. impl_renames += 1
  96. print(
  97. "Renaming resulted in %d API files and %d impl files."
  98. % (api_renames, impl_renames)
  99. )
  100. if __name__ == "__main__":
  101. _Workflow().run()