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.

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