package_manager.py 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. import os
  2. import subprocess
  3. import sys
  4. from abc import ABCMeta, abstractmethod
  5. from six import add_metaclass
  6. from .constants import NPM_REGISTRY_URL
  7. from .package_json import PackageJson
  8. from .utils import build_nm_path, build_pj_path
  9. from .timeit import timeit
  10. class PackageManagerError(RuntimeError):
  11. pass
  12. class PackageManagerCommandError(PackageManagerError):
  13. def __init__(self, cmd, code, stdout, stderr):
  14. self.cmd = cmd
  15. self.code = code
  16. self.stdout = stdout
  17. self.stderr = stderr
  18. msg = "package manager exited with code {} while running {}:\n{}\n{}".format(code, cmd, stdout, stderr)
  19. super(PackageManagerCommandError, self).__init__(msg)
  20. @add_metaclass(ABCMeta)
  21. class BasePackageManager(object):
  22. def __init__(
  23. self,
  24. build_root,
  25. build_path,
  26. sources_path,
  27. nodejs_bin_path,
  28. script_path,
  29. module_path=None,
  30. sources_root=None,
  31. ):
  32. self.module_path = build_path[len(build_root) + 1 :] if module_path is None else module_path
  33. self.build_path = build_path
  34. self.sources_path = sources_path
  35. self.build_root = build_root
  36. self.sources_root = sources_path[: -len(self.module_path) - 1] if sources_root is None else sources_root
  37. self.nodejs_bin_path = nodejs_bin_path
  38. self.script_path = script_path
  39. @classmethod
  40. def load_package_json(cls, path):
  41. """
  42. :param path: path to package.json
  43. :type path: str
  44. :rtype: PackageJson
  45. """
  46. return PackageJson.load(path)
  47. @classmethod
  48. def load_package_json_from_dir(cls, dir_path):
  49. """
  50. :param dir_path: path to directory with package.json
  51. :type dir_path: str
  52. :rtype: PackageJson
  53. """
  54. return cls.load_package_json(build_pj_path(dir_path))
  55. def _build_package_json(self):
  56. """
  57. :rtype: PackageJson
  58. """
  59. pj = self.load_package_json_from_dir(self.sources_path)
  60. if not os.path.exists(self.build_path):
  61. os.makedirs(self.build_path, exist_ok=True)
  62. pj.path = build_pj_path(self.build_path)
  63. pj.write()
  64. return pj
  65. @classmethod
  66. @abstractmethod
  67. def load_lockfile(cls, path):
  68. pass
  69. @classmethod
  70. @abstractmethod
  71. def load_lockfile_from_dir(cls, dir_path):
  72. pass
  73. @abstractmethod
  74. def create_node_modules(self, yatool_prebuilder_path=None, local_cli=False, bundle=True):
  75. pass
  76. @abstractmethod
  77. def extract_packages_meta_from_lockfiles(self, lf_paths):
  78. pass
  79. @abstractmethod
  80. def calc_prepare_deps_inouts_and_resources(
  81. self, store_path: str, has_deps: bool
  82. ) -> tuple[list[str], list[str], list[str]]:
  83. pass
  84. @abstractmethod
  85. def calc_node_modules_inouts(self, local_cli: bool, has_deps: bool) -> tuple[list[str], list[str]]:
  86. pass
  87. @abstractmethod
  88. def build_workspace(self, tarballs_store: str):
  89. pass
  90. def get_local_peers_from_package_json(self):
  91. """
  92. Returns paths of direct workspace dependencies (source root related).
  93. :rtype: list of str
  94. """
  95. return self.load_package_json_from_dir(self.sources_path).get_workspace_dep_paths(base_path=self.module_path)
  96. def get_peers_from_package_json(self):
  97. """
  98. Returns paths of workspace dependencies (source root related).
  99. :rtype: list of str
  100. """
  101. pj = self.load_package_json_from_dir(self.sources_path)
  102. prefix_len = len(self.sources_root) + 1
  103. return [p[prefix_len:] for p in pj.get_workspace_map(ignore_self=True).keys()]
  104. @timeit
  105. def _exec_command(self, args, cwd: str, include_defaults=True, script_path=None, env=None):
  106. if not self.nodejs_bin_path:
  107. raise PackageManagerError("Unable to execute command: nodejs_bin_path is not configured")
  108. cmd = (
  109. [self.nodejs_bin_path, script_path or self.script_path]
  110. + args
  111. + (self._get_default_options() if include_defaults else [])
  112. )
  113. p = subprocess.Popen(cmd, cwd=cwd, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)
  114. stdout, stderr = p.communicate()
  115. if p.returncode != 0:
  116. self._dump_debug_log()
  117. raise PackageManagerCommandError(cmd, p.returncode, stdout.decode("utf-8"), stderr.decode("utf-8"))
  118. def _nm_path(self, *parts):
  119. return os.path.join(build_nm_path(self.build_path), *parts)
  120. def _tarballs_store_path(self, pkg, store_path):
  121. return os.path.join(self.module_path, store_path, pkg.tarball_path)
  122. def _get_default_options(self):
  123. return ["--registry", NPM_REGISTRY_URL]
  124. def _get_debug_log_path(self):
  125. return None
  126. def _dump_debug_log(self):
  127. log_path = self._get_debug_log_path()
  128. if not log_path:
  129. return
  130. try:
  131. with open(log_path) as f:
  132. sys.stderr.write("Package manager log {}:\n{}\n".format(log_path, f.read()))
  133. except Exception:
  134. sys.stderr.write("Failed to dump package manager log {}.\n".format(log_path))