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.

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