選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

100 行
2.7KB

  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-p4',
  11. ]
  12. HERE = Path(__file__).parent.absolute()
  13. PROJECT_ROOT = HERE.parent
  14. BUILD_DIR = PROJECT_ROOT / '_build'
  15. BOOTSTRAP_BASE_DIR = BUILD_DIR / '_bootstrap'
  16. PREBUILT_DIR = PROJECT_ROOT / '_prebuilt'
  17. EXE_SUFFIX = '.exe' if os.name == 'nt' else ''
  18. def _run_quiet(cmd, **kwargs) -> None:
  19. cmd = [str(s) for s in cmd]
  20. res = subprocess.run(
  21. cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, **kwargs)
  22. if res.returncode != 0:
  23. print(f'Subprocess command {cmd} failed '
  24. f'[{res.returncode}]:\n{res.stdout.decode()}')
  25. raise subprocess.CalledProcessError(res.returncode, cmd)
  26. def _clone_bootstrap_phase(ph: str) -> Path:
  27. print(f'Clone revision: {ph}')
  28. bts_dir = BOOTSTRAP_BASE_DIR / ph
  29. if bts_dir.exists():
  30. shutil.rmtree(bts_dir)
  31. _run_quiet([
  32. 'git',
  33. 'clone',
  34. '--depth=1',
  35. f'--branch={ph}',
  36. f'file://{PROJECT_ROOT}',
  37. bts_dir,
  38. ])
  39. return bts_dir
  40. def _build_bootstrap_phase(ph: str, bts_dir: Path,
  41. args: argparse.Namespace) -> None:
  42. print(f'Build revision: {ph} [This may take a moment]')
  43. env = os.environ.copy()
  44. env['DDS_BOOTSTRAP_PREV_EXE'] = str(PREBUILT_DIR / 'dds')
  45. _run_quiet(
  46. [
  47. sys.executable,
  48. '-u',
  49. str(bts_dir / 'tools/build.py'),
  50. f'--cxx={args.cxx}',
  51. ],
  52. env=env,
  53. cwd=bts_dir,
  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:]))