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.

86 satır
2.5KB

  1. import enum
  2. from pathlib import Path
  3. from contextlib import contextmanager
  4. from typing import Iterator
  5. import sys
  6. import urllib.request
  7. import shutil
  8. from . import paths
  9. from .dds import DDSWrapper
  10. from .paths import new_tempdir
  11. class BootstrapMode(enum.Enum):
  12. """How should be bootstrap our prior DDS executable?"""
  13. #: Downlaod one from GitHub
  14. Download = 'download'
  15. #: Build one from source
  16. Build = 'build'
  17. #: Skip bootstrapping. Assume it already exists.
  18. Skip = 'skip'
  19. #: If the prior executable exists, skip, otherwise download
  20. Lazy = 'lazy'
  21. def _do_bootstrap_download() -> Path:
  22. filename = {
  23. 'win32': 'dds-win-x64.exe',
  24. 'linux': 'dds-linux-x64',
  25. 'darwin': 'dds-macos-x64',
  26. 'freebsd11': 'dds-freebsd-x64',
  27. 'freebsd12': 'dds-freebsd-x64',
  28. }.get(sys.platform)
  29. if filename is None:
  30. raise RuntimeError(f'We do not have a prebuilt DDS binary for the "{sys.platform}" platform')
  31. url = f'https://github.com/vector-of-bool/dds/releases/download/0.1.0-alpha.4/{filename}'
  32. print(f'Downloading prebuilt DDS executable: {url}')
  33. stream = urllib.request.urlopen(url)
  34. paths.PREBUILT_DDS.parent.mkdir(exist_ok=True, parents=True)
  35. with paths.PREBUILT_DDS.open('wb') as fd:
  36. while True:
  37. buf = stream.read(1024 * 4)
  38. if not buf:
  39. break
  40. fd.write(buf)
  41. if sys.platform != 'win32':
  42. # Mark the binary executable. By default it won't be
  43. mode = paths.PREBUILT_DDS.stat().st_mode
  44. mode |= 0b001_001_001
  45. paths.PREBUILT_DDS.chmod(mode)
  46. return paths.PREBUILT_DDS
  47. @contextmanager
  48. def pin_exe(fpath: Path) -> Iterator[Path]:
  49. """
  50. Create a copy of 'fpath' at an unspecified location, and yield that path.
  51. This is needed if the executable would overwrite itself.
  52. """
  53. with new_tempdir() as tdir:
  54. tfile = tdir / 'previous-dds.exe'
  55. shutil.copy2(fpath, tfile)
  56. yield tfile
  57. @contextmanager
  58. def get_bootstrap_exe(mode: BootstrapMode) -> Iterator[DDSWrapper]:
  59. """Context manager that yields a DDSWrapper around a prior 'dds' executable"""
  60. if mode is BootstrapMode.Lazy:
  61. f = paths.PREBUILT_DDS
  62. if not f.exists():
  63. _do_bootstrap_download()
  64. elif mode is BootstrapMode.Download:
  65. f = _do_bootstrap_download()
  66. elif mode is BootstrapMode.Build:
  67. f = _do_bootstrap_build() # type: ignore # TODO
  68. elif mode is BootstrapMode.Skip:
  69. f = paths.PREBUILT_DDS
  70. with pin_exe(f) as dds:
  71. yield DDSWrapper(dds)