leveldb_patch.py 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. # Copyright 2022 Google LLC
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. """
  15. Modify the CMakeLists.txt from LevelDb to statically link Snappy compression
  16. support.
  17. """
  18. import argparse
  19. import dataclasses
  20. import os
  21. import pathlib
  22. import subprocess
  23. from typing import Iterable, Sequence
  24. def main() -> None:
  25. arg_parser = argparse.ArgumentParser()
  26. arg_parser.add_argument("--snappy-source-dir", required=True)
  27. arg_parser.add_argument("--snappy-binary-dir", required=True)
  28. arg_parser.add_argument("--additional-patch-file", required=False)
  29. parsed_args = arg_parser.parse_args()
  30. del arg_parser
  31. snappy_source_dir = pathlib.Path(parsed_args.snappy_source_dir)
  32. snappy_binary_dir = pathlib.Path(parsed_args.snappy_binary_dir)
  33. additional_patch_file = parsed_args.additional_patch_file
  34. del parsed_args
  35. cmakelists_txt_file = pathlib.Path("CMakeLists.txt")
  36. with cmakelists_txt_file.open("rt", encoding="utf8") as f:
  37. lines = tuple(f)
  38. patcher = CMakeListsPatcher(
  39. lines,
  40. os.path.abspath(__file__),
  41. snappy_source_dir,
  42. snappy_binary_dir,
  43. )
  44. patched_lines = tuple(patcher.patch())
  45. with cmakelists_txt_file.open("wt", encoding="utf8") as f:
  46. f.writelines(patched_lines)
  47. additional_patch_stamp = 'leveldb_additional_patch_stamp'
  48. if additional_patch_file and not os.path.exists(additional_patch_stamp):
  49. print("Applying patch %s" % additional_patch_file)
  50. subprocess.run(['git', 'apply', '-v', additional_patch_file],
  51. check=True)
  52. # Create a stamp file so the patch isn't applied twice.
  53. open(additional_patch_stamp, 'a').close()
  54. @dataclasses.dataclass(frozen=True)
  55. class LineComponents:
  56. full: str
  57. indent: str
  58. line: str
  59. eol: str
  60. class CMakeListsPatcher:
  61. SNAPPY_DETECT_LINE = \
  62. """check_library_exists(snappy snappy_compress "" HAVE_SNAPPY)"""
  63. SNAPPY_INCLUDE_LINE = \
  64. "target_include_directories(leveldb"
  65. SNAPPY_LINK_LINE = \
  66. "target_link_libraries(leveldb snappy)"
  67. def __init__(
  68. self,
  69. lines: Sequence[str],
  70. script_name: str,
  71. snappy_source_dir: pathlib.Path,
  72. snappy_binary_dir: pathlib.Path) -> None:
  73. self.i = 0
  74. self.lines = lines
  75. self.script_name = script_name
  76. self.snappy_source_dir_str = snappy_source_dir.as_posix()
  77. self.snappy_binary_dir_str = snappy_binary_dir.as_posix()
  78. def patch(self) -> Iterable[str]:
  79. while self.i < len(self.lines):
  80. full_line = self.lines[self.i]
  81. line = self._split_line(full_line)
  82. self.i += 1
  83. if line.line == self.SNAPPY_DETECT_LINE:
  84. yield from self._on_snappy_detect_line(line)
  85. elif line.line == self.SNAPPY_INCLUDE_LINE:
  86. yield full_line
  87. yield from self._on_leveldb_include_start()
  88. elif line.line == self.SNAPPY_LINK_LINE:
  89. yield from self._on_leveldb_snappy_link_line(line)
  90. else:
  91. yield full_line
  92. def _begin_mod_line(self, mod_name: str) -> str:
  93. return f"# BEGIN: {mod_name} modification by {self.script_name}"
  94. def _end_mod_line(self, mod_name: str) -> str:
  95. return f"# END: {mod_name} modification by {self.script_name}"
  96. def _on_snappy_detect_line(self, line: LineComponents) -> Iterable[str]:
  97. yield self._begin_mod_line("snappy_detect_line") + line.eol
  98. yield line.indent + "# " + line.line + line.eol
  99. yield line.indent + """set(HAVE_SNAPPY ON CACHE BOOL "")""" + line.eol
  100. yield self._end_mod_line("snappy_detect_line") + line.eol
  101. def _on_leveldb_include_start(self) -> Iterable[str]:
  102. line1 = self._split_line(self.lines[self.i])
  103. line2 = self._split_line(self.lines[self.i + 1])
  104. begin_mod_line = self._begin_mod_line("leveldb_include_start")
  105. if line1.line == begin_mod_line:
  106. return
  107. yield begin_mod_line + line1.eol
  108. yield line1.indent + "PRIVATE" + line1.eol
  109. yield line2.indent + self.snappy_source_dir_str + line2.eol
  110. yield line2.indent + self.snappy_binary_dir_str + line2.eol
  111. yield self._end_mod_line("leveldb_include_start") + line1.eol
  112. def _on_leveldb_snappy_link_line(self, line: LineComponents) -> Iterable[str]:
  113. yield self._begin_mod_line("leveldb_snappy_link_line") + line.eol
  114. yield line.indent + "# " + line.line + line.eol
  115. yield line.indent + f"target_link_libraries(leveldb Snappy::Snappy)" + line.eol
  116. yield self._end_mod_line("leveldb_snappy_link_line") + line.eol
  117. def _split_line(self, line: str) -> LineComponents:
  118. line_rstripped = line.rstrip()
  119. eol = line[len(line_rstripped):]
  120. line_stripped = line_rstripped.strip()
  121. indent = line_rstripped[:len(line_rstripped) - len(line_stripped)]
  122. return LineComponents(full=line, indent=indent, line=line_stripped, eol=eol)
  123. class LeveDbPatchException(Exception):
  124. pass
  125. if __name__ == "__main__":
  126. main()