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.

bootstrap.py 2.9KB

5 jaren geleden
5 jaren geleden
5 jaren geleden
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. import argparse
  2. from pathlib import Path
  3. import subprocess
  4. import os
  5. from typing import Sequence, NamedTuple
  6. import sys
  7. import shutil
  8. class BootstrapPhase(NamedTuple):
  9. ref: str
  10. nix_compiler: str
  11. win_compiler: str
  12. @property
  13. def platform_compiler(self):
  14. if os.name == 'nt':
  15. return self.win_compiler
  16. else:
  17. return self.nix_compiler
  18. BOOTSTRAP_PHASES = [
  19. BootstrapPhase('bootstrap-p1', 'g++-8', 'cl.exe'),
  20. BootstrapPhase('bootstrap-p4', 'g++-8', 'cl.exe'),
  21. BootstrapPhase('bootstrap-p5.1', 'g++-9', 'cl.exe'),
  22. ]
  23. HERE = Path(__file__).parent.absolute()
  24. PROJECT_ROOT = HERE.parent
  25. BUILD_DIR = PROJECT_ROOT / '_build'
  26. BOOTSTRAP_BASE_DIR = BUILD_DIR / '_bootstrap'
  27. PREBUILT_DIR = PROJECT_ROOT / '_prebuilt'
  28. EXE_SUFFIX = '.exe' if os.name == 'nt' else ''
  29. def _run_quiet(cmd, **kwargs) -> None:
  30. cmd = [str(s) for s in cmd]
  31. res = subprocess.run(
  32. cmd,
  33. stdout=subprocess.PIPE,
  34. stderr=subprocess.STDOUT,
  35. **kwargs,
  36. )
  37. if res.returncode != 0:
  38. print(f'Subprocess command {cmd} failed '
  39. f'[{res.returncode}]:\n{res.stdout.decode()}')
  40. raise subprocess.CalledProcessError(res.returncode, cmd)
  41. def _clone_bootstrap_phase(ref: str) -> Path:
  42. print(f'Clone revision: {ref}')
  43. bts_dir = BOOTSTRAP_BASE_DIR / ref
  44. if bts_dir.exists():
  45. shutil.rmtree(bts_dir)
  46. _run_quiet([
  47. 'git',
  48. 'clone',
  49. '--depth=1',
  50. f'--branch={ref}',
  51. f'file://{PROJECT_ROOT}',
  52. bts_dir,
  53. ])
  54. return bts_dir
  55. def _build_bootstrap_phase(ph: BootstrapPhase, bts_dir: Path) -> None:
  56. print(f'Build revision: {ph.ref} [This may take a moment]')
  57. env = os.environ.copy()
  58. env['DDS_BOOTSTRAP_PREV_EXE'] = str(PREBUILT_DIR / F'dds{EXE_SUFFIX}')
  59. _run_quiet(
  60. [
  61. sys.executable,
  62. '-u',
  63. str(bts_dir / 'tools/build.py'),
  64. f'--cxx={ph.platform_compiler}',
  65. ],
  66. env=env,
  67. cwd=bts_dir,
  68. )
  69. def _pull_executable(bts_dir: Path) -> Path:
  70. prebuild_dir = (PROJECT_ROOT / '_prebuilt')
  71. prebuild_dir.mkdir(exist_ok=True)
  72. generated = list(bts_dir.glob(f'_build/dds{EXE_SUFFIX}'))
  73. assert len(generated) == 1, repr(generated)
  74. exe, = generated
  75. dest = prebuild_dir / exe.name
  76. if dest.exists():
  77. dest.unlink()
  78. exe.rename(dest)
  79. return dest
  80. def _run_boot_phase(phase: BootstrapPhase) -> Path:
  81. bts_dir = _clone_bootstrap_phase(phase.ref)
  82. _build_bootstrap_phase(phase, bts_dir)
  83. return _pull_executable(bts_dir)
  84. def main(argv: Sequence[str]) -> int:
  85. for idx, phase in enumerate(BOOTSTRAP_PHASES):
  86. print(f'Bootstrap phase [{idx+1}/{len(BOOTSTRAP_PHASES)}]')
  87. exe = _run_boot_phase(phase)
  88. print(f'A bootstrapped DDS executable has been generated: {exe}')
  89. return 0
  90. if __name__ == "__main__":
  91. sys.exit(main(sys.argv[1:]))