You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

96 lines
3.7 KiB

  1. # vim: fileencoding=utf-8
  2. #
  3. from .base_format import Format
  4. from calculate.utils.files import Process
  5. from calculate.templates.format.base_format import FormatError
  6. from ..template_engine import ParametersContainer, Variables
  7. from ...variables.datavars import NamespaceNode
  8. from ...variables.loader import Datavars
  9. from typing import Union
  10. from os import path
  11. class PatchFormat(Format):
  12. FORMAT = 'patch'
  13. EXECUTABLE = True
  14. def __init__(self, patch_text: str,
  15. template_path: str,
  16. parameters: ParametersContainer,
  17. datavars: Union[Datavars, NamespaceNode, Variables]):
  18. self._patch_text = patch_text
  19. self._cwd_path = '/'
  20. self._last_level = 0
  21. # Измененные файлы.
  22. self.changed_files = dict()
  23. # Предупреждения.
  24. self._warnings: list = []
  25. def execute_format(self, target_path, chroot_path='/'):
  26. '''Метод для запуска работы формата.'''
  27. self._cwd_path = target_path
  28. if not path.isdir(self._cwd_path):
  29. # Если target_path -- путь к файлу, запускаем все процессы из
  30. # директории, в которой этот файл находится.
  31. self._cwd_path = path.dirname(self._cwd_path)
  32. if not path.exists(self._cwd_path):
  33. raise FormatError('root path does not exist', executable=True)
  34. if self._patch_text:
  35. self._patch_document()
  36. return self.changed_files
  37. else:
  38. raise FormatError('empty patch file', executable=True)
  39. def _patch_document(self):
  40. '''Метод, производящий наложение патча путем запуска процесса patch.'''
  41. # Сначала определяем на каком уровне накладываем патч.
  42. # Для этого запускаем утилиту patch с --dry-run и смотрим результат
  43. # выполнения.
  44. for level in range(0, 4):
  45. patch_dry_run = Process('patch', '--dry-run',
  46. '-p{}'.format(level),
  47. cwd=self._cwd_path)
  48. patch_dry_run.write(self._patch_text)
  49. if patch_dry_run.success():
  50. break
  51. patch_dry_run = Process('patch', '-R', '--dry-run',
  52. '-p{}'.format(level),
  53. cwd=self._cwd_path)
  54. patch_dry_run.write(self._patch_text)
  55. if patch_dry_run.success():
  56. return ''
  57. else:
  58. raise FormatError('correction failed', executable=True)
  59. self._last_level = level
  60. patch_run = Process('patch', '-p{}'.format(level),
  61. cwd=self._cwd_path)
  62. patch_run.write(self._patch_text)
  63. if patch_run.success():
  64. for line in patch_run.read_lines():
  65. if line.startswith('patching file'):
  66. changed_file_path = path.join(self._cwd_path,
  67. line[13:].strip())
  68. if path.exists(changed_file_path):
  69. self.changed_files.update({changed_file_path:
  70. 'C'})
  71. else:
  72. self.changed_files.update({changed_file_path:
  73. 'D'})
  74. return patch_run.read()
  75. else:
  76. return ''
  77. def __bool__(self):
  78. return bool(self._patch_text)
  79. @property
  80. def warnings(self):
  81. return self._warnings