Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

build.py 4.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. #!/usr/bin/env python3
  2. import argparse
  3. from pathlib import Path
  4. import multiprocessing
  5. import itertools
  6. from concurrent.futures import ThreadPoolExecutor
  7. from typing import Sequence, Iterable, Dict, Tuple
  8. import subprocess
  9. import time
  10. import sys
  11. HERE_DIR = Path(__file__).parent.absolute()
  12. INCLUDE_DIRS = [
  13. 'external/taywee-args/include',
  14. 'external/spdlog/include',
  15. ]
  16. def _compile_src(cxx: Path, cpp_file: Path) -> Tuple[Path, Path]:
  17. build_dir = HERE_DIR / '_build'
  18. src_dir = HERE_DIR / 'src'
  19. relpath = cpp_file.relative_to(src_dir)
  20. obj_path = build_dir / relpath.with_name(relpath.name + '.o')
  21. obj_path.parent.mkdir(exist_ok=True, parents=True)
  22. cmd = [
  23. cxx,
  24. '-std=c++17',
  25. '-static',
  26. '-Wall',
  27. '-Wextra',
  28. '-Werror',
  29. '-Wshadow',
  30. '-Wconversion',
  31. '-fdiagnostics-color',
  32. '-pthread',
  33. '-g',
  34. '-c',
  35. '-O0',
  36. f'-I{src_dir}',
  37. cpp_file,
  38. f'-o{obj_path}',
  39. ]
  40. cmd.extend(
  41. itertools.chain.from_iterable(
  42. ('-isystem', HERE_DIR / subdir) for subdir in INCLUDE_DIRS))
  43. msg = f'Compile C++ file: {cpp_file}'
  44. print(msg)
  45. start = time.time()
  46. res = subprocess.run(
  47. cmd,
  48. stdout=subprocess.PIPE,
  49. stderr=subprocess.STDOUT,
  50. )
  51. if res.returncode != 0:
  52. raise RuntimeError(
  53. f'Compile command ({cmd}) failed for {cpp_file}:\n{res.stdout.decode()}'
  54. )
  55. end = time.time()
  56. print(f'{msg} - Done: {end - start:.2}s')
  57. return cpp_file, obj_path
  58. def compile_sources(cxx: Path, sources: Iterable[Path]) -> Dict[Path, Path]:
  59. pool = ThreadPoolExecutor(multiprocessing.cpu_count() + 2)
  60. return {
  61. src: obj
  62. for src, obj in pool.map(lambda s: _compile_src(cxx, s), sources)
  63. }
  64. def make_library(objects: Iterable[Path]) -> Path:
  65. lib_file = HERE_DIR / '_build/libddslim.a'
  66. cmd = ['ar', 'rsc', lib_file]
  67. cmd.extend(objects)
  68. if lib_file.exists():
  69. lib_file.unlink()
  70. print(f'Creating static library {lib_file}')
  71. subprocess.check_call(cmd)
  72. return lib_file
  73. def link_exe(cxx: Path, obj: Path, lib: Path, *, out: Path = None) -> Path:
  74. if out is None:
  75. basename = obj.stem
  76. out = HERE_DIR / '_build/test' / (basename + '.exe')
  77. out.parent.mkdir(exist_ok=True, parents=True)
  78. print(f'Linking executable {out}')
  79. subprocess.check_call([
  80. cxx,
  81. '-static',
  82. '-pthread',
  83. # See: https://stackoverflow.com/questions/35116327/when-g-static-link-pthread-cause-segmentation-fault-why
  84. '-Wl,--whole-archive',
  85. '-lpthread',
  86. '-Wl,--no-whole-archive',
  87. obj,
  88. lib,
  89. '-lstdc++fs',
  90. f'-o{out}',
  91. ])
  92. return out
  93. def run_test(exe: Path) -> None:
  94. print(f'Running test: {exe}')
  95. subprocess.check_call([exe])
  96. def main(argv: Sequence[str]) -> int:
  97. parser = argparse.ArgumentParser()
  98. parser.add_argument(
  99. '--test', action='store_true', help='Build and run tests')
  100. parser.add_argument(
  101. '--cxx', help='Path/name of the C++ compiler to use.', required=True)
  102. args = parser.parse_args(argv)
  103. all_sources = set(HERE_DIR.glob('src/**/*.cpp'))
  104. test_sources = set(HERE_DIR.glob('src/**/*.test.cpp'))
  105. main_sources = set(HERE_DIR.glob('src/**/*.main.cpp'))
  106. lib_sources = (all_sources - test_sources) - main_sources
  107. objects = compile_sources(Path(args.cxx), all_sources)
  108. lib = make_library(objects[p] for p in lib_sources)
  109. test_objs = (objects[p] for p in test_sources)
  110. pool = ThreadPoolExecutor(multiprocessing.cpu_count() + 2)
  111. test_exes = list(
  112. pool.map(lambda o: link_exe(Path(args.cxx), o, lib), test_objs))
  113. main_exe = link_exe(
  114. Path(args.cxx),
  115. objects[next(iter(main_sources))],
  116. lib,
  117. out=HERE_DIR / '_build/ddslim')
  118. if args.test:
  119. list(pool.map(run_test, test_exes))
  120. print(f'Main executable generated at {main_exe}')
  121. return 0
  122. if __name__ == "__main__":
  123. sys.exit(main(sys.argv[1:]))