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.

167 lines
5.6KB

  1. import multiprocessing
  2. import shutil
  3. import os
  4. from pathlib import Path
  5. import copy
  6. from typing import Optional, TypeVar, Iterable
  7. from . import paths, proc, toolchain as tc_mod
  8. from dds_ci.util import Pathish
  9. T = TypeVar('T')
  10. class DDSWrapper:
  11. """
  12. Wraps a 'dds' executable with some convenience APIs that invoke various
  13. 'dds' subcommands.
  14. """
  15. def __init__(self,
  16. path: Path,
  17. *,
  18. repo_dir: Optional[Pathish] = None,
  19. pkg_db_path: Optional[Pathish] = None,
  20. default_cwd: Optional[Pathish] = None) -> None:
  21. self.path = path
  22. self.repo_dir = Path(repo_dir or (paths.PREBUILT_DIR / 'ci-repo'))
  23. self.pkg_db_path = Path(pkg_db_path or (self.repo_dir.parent / 'ci-catalog.db'))
  24. self.default_cwd = default_cwd or Path.cwd()
  25. def clone(self: T) -> T:
  26. return copy.deepcopy(self)
  27. @property
  28. def catalog_path_arg(self) -> str:
  29. """The arguments for --catalog"""
  30. return f'--catalog={self.pkg_db_path}'
  31. @property
  32. def repo_dir_arg(self) -> str:
  33. """The arguments for --repo-dir"""
  34. return f'--repo-dir={self.repo_dir}'
  35. @property
  36. def project_dir_flag(self) -> str:
  37. return '--project-dir'
  38. def set_repo_scratch(self, path: Pathish) -> None:
  39. self.repo_dir = Path(path) / 'data'
  40. self.pkg_db_path = Path(path) / 'pkgs.db'
  41. def clean(self, *, build_dir: Optional[Path] = None, repo: bool = True, pkg_db: bool = True) -> None:
  42. """
  43. Clean out prior executable output, including repos, pkg_db, and
  44. the build results at 'build_dir', if given.
  45. """
  46. if build_dir and build_dir.exists():
  47. shutil.rmtree(build_dir)
  48. if repo and self.repo_dir.exists():
  49. shutil.rmtree(self.repo_dir)
  50. if pkg_db and self.pkg_db_path.exists():
  51. self.pkg_db_path.unlink()
  52. def run(self, args: proc.CommandLine, *, cwd: Optional[Pathish] = None, timeout: Optional[int] = None) -> None:
  53. """Execute the 'dds' executable with the given arguments"""
  54. env = os.environ.copy()
  55. env['DDS_NO_ADD_INITIAL_REPO'] = '1'
  56. proc.check_run([self.path, args], cwd=cwd or self.default_cwd, env=env, timeout=timeout)
  57. def catalog_json_import(self, path: Path) -> None:
  58. """Run 'catalog import' to import the given JSON. Only applicable to older 'dds'"""
  59. self.run(['catalog', 'import', self.catalog_path_arg, f'--json={path}'])
  60. def catalog_get(self, what: str) -> None:
  61. self.run(['catalog', 'get', self.catalog_path_arg, what])
  62. def pkg_get(self, what: str) -> None:
  63. self.run(['pkg', 'get', self.catalog_path_arg, what])
  64. def repo_add(self, url: str) -> None:
  65. self.run(['pkg', 'repo', 'add', self.catalog_path_arg, url])
  66. def repo_remove(self, name: str) -> None:
  67. self.run(['pkg', 'repo', 'remove', self.catalog_path_arg, name])
  68. def repo_import(self, sdist: Path) -> None:
  69. self.run(['repo', self.repo_dir_arg, 'import', sdist])
  70. def pkg_import(self, filepath: Pathish) -> None:
  71. self.run(['pkg', 'import', filepath, self.repo_dir_arg])
  72. def build(self,
  73. *,
  74. root: Path,
  75. toolchain: Optional[Path] = None,
  76. build_root: Optional[Path] = None,
  77. jobs: Optional[int] = None,
  78. more_args: Optional[proc.CommandLine] = None,
  79. timeout: Optional[int] = None) -> None:
  80. """
  81. Run 'dds build' with the given arguments.
  82. :param toolchain: The toolchain to use for the build.
  83. :param root: The root project directory.
  84. :param build_root: The root directory where the output will be written.
  85. :param jobs: The number of jobs to use. Default is CPU-count + 2
  86. """
  87. toolchain = toolchain or tc_mod.get_default_audit_toolchain()
  88. jobs = jobs or multiprocessing.cpu_count() + 2
  89. self.run(
  90. [
  91. 'build',
  92. f'--toolchain={toolchain}',
  93. self.repo_dir_arg,
  94. self.catalog_path_arg,
  95. f'--jobs={jobs}',
  96. f'{self.project_dir_flag}={root}',
  97. f'--out={build_root}',
  98. more_args or (),
  99. ],
  100. timeout=timeout,
  101. )
  102. def compile_file(self,
  103. paths: Iterable[Pathish],
  104. *,
  105. toolchain: Optional[Pathish] = None,
  106. project_dir: Pathish,
  107. out: Optional[Pathish] = None) -> None:
  108. """
  109. Run 'dds compile-file' for the given paths.
  110. """
  111. toolchain = toolchain or tc_mod.get_default_audit_toolchain()
  112. self.run([
  113. 'compile-file',
  114. paths,
  115. f'--toolchain={toolchain}',
  116. f'{self.project_dir_flag}={project_dir}',
  117. f'--out={out}',
  118. ])
  119. def build_deps(self, args: proc.CommandLine, *, toolchain: Optional[Path] = None) -> None:
  120. toolchain = toolchain or tc_mod.get_default_audit_toolchain()
  121. self.run([
  122. 'build-deps',
  123. f'--toolchain={toolchain}',
  124. self.catalog_path_arg,
  125. self.repo_dir_arg,
  126. args,
  127. ])
  128. class NewDDSWrapper(DDSWrapper):
  129. """
  130. Wraps the new 'dds' executable with some convenience APIs
  131. """
  132. @property
  133. def repo_dir_arg(self) -> str:
  134. return f'--pkg-cache-dir={self.repo_dir}'
  135. @property
  136. def catalog_path_arg(self) -> str:
  137. return f'--pkg-db-path={self.pkg_db_path}'
  138. @property
  139. def project_dir_flag(self) -> str:
  140. return '--project'