build_llvm.py 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. #!/usr/bin/env python3
  2. #
  3. # Copyright (C) 2019 Intel Corporation. All rights reserved.
  4. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  5. #
  6. import argparse
  7. import os
  8. import pathlib
  9. import shlex
  10. import shutil
  11. import subprocess
  12. import sysconfig
  13. import sys
  14. def clone_llvm(dst_dir, llvm_repo, llvm_branch):
  15. """
  16. any error will raise CallProcessError
  17. """
  18. llvm_dir = dst_dir.joinpath("llvm").resolve()
  19. if not llvm_dir.exists():
  20. print(f"Clone llvm to {llvm_dir} ...")
  21. GIT_CLONE_CMD = f"git clone --depth 1 --branch {llvm_branch} {llvm_repo} llvm"
  22. subprocess.check_output(shlex.split(GIT_CLONE_CMD), cwd=dst_dir)
  23. else:
  24. print(f"There is an LLVM local repo in {llvm_dir}, clean and keep using it")
  25. return llvm_dir
  26. def build_llvm(llvm_dir, platform, backends, projects):
  27. LLVM_COMPILE_OPTIONS = [
  28. '-DCMAKE_BUILD_TYPE:STRING="Release"',
  29. "-DCMAKE_EXPORT_COMPILE_COMMANDS=ON",
  30. "-DLLVM_APPEND_VC_REV:BOOL=ON",
  31. "-DLLVM_BUILD_BENCHMARKS:BOOL=OFF",
  32. "-DLLVM_BUILD_DOCS:BOOL=OFF",
  33. "-DLLVM_BUILD_EXAMPLES:BOOL=OFF",
  34. "-DLLVM_BUILD_LLVM_DYLIB:BOOL=OFF",
  35. "-DLLVM_BUILD_TESTS:BOOL=OFF",
  36. "-DLLVM_CCACHE_BUILD:BOOL=OFF",
  37. "-DLLVM_ENABLE_BINDINGS:BOOL=OFF",
  38. "-DLLVM_ENABLE_IDE:BOOL=OFF",
  39. "-DLLVM_ENABLE_TERMINFO:BOOL=OFF",
  40. "-DLLVM_ENABLE_ZLIB:BOOL=OFF",
  41. "-DLLVM_INCLUDE_BENCHMARKS:BOOL=OFF",
  42. "-DLLVM_INCLUDE_DOCS:BOOL=OFF",
  43. "-DLLVM_INCLUDE_EXAMPLES:BOOL=OFF",
  44. "-DLLVM_INCLUDE_UTILS:BOOL=OFF",
  45. "-DLLVM_INCLUDE_TESTS:BOOL=OFF",
  46. "-DLLVM_BUILD_TESTS:BOOL=OFF",
  47. "-DLLVM_OPTIMIZED_TABLEGEN:BOOL=ON",
  48. ]
  49. LLVM_EXTRA_COMPILE_OPTIONS = {
  50. "arc": [
  51. '-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD:STRING="ARC"',
  52. "-DLLVM_ENABLE_LIBICUUC:BOOL=OFF",
  53. "-DLLVM_ENABLE_LIBICUDATA:BOOL=OFF",
  54. ],
  55. "xtensa": [
  56. '-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD:STRING="Xtensa"',
  57. ],
  58. "windows": [
  59. "-DCMAKE_INSTALL_PREFIX=LLVM-install",
  60. ],
  61. "default": [],
  62. }
  63. LLVM_TARGETS_TO_BUILD = [
  64. '-DLLVM_TARGETS_TO_BUILD:STRING="' + ";".join(backends) + '"'
  65. if backends
  66. else '-DLLVM_TARGETS_TO_BUILD:STRING="AArch64;ARM;Mips;RISCV;X86"'
  67. ]
  68. LLVM_PROJECTS_TO_BUILD = [
  69. '-DLLVM_ENABLE_PROJECTS:STRING="' + ";".join(projects) + '"' if projects else ""
  70. ]
  71. # lldb project requires libxml2
  72. LLVM_LIBXML2_OPTION = [
  73. "-DLLVM_ENABLE_LIBXML2:BOOL=" + ("ON" if "lldb" in projects else "OFF")
  74. ]
  75. # enabling LLVM_INCLUDE_TOOLS will increase ~300M to the final package
  76. LLVM_INCLUDE_TOOLS_OPTION = [
  77. "-DLLVM_INCLUDE_TOOLS:BOOL=ON" if projects else "-DLLVM_INCLUDE_TOOLS:BOOL=OFF"
  78. ]
  79. if not llvm_dir.exists():
  80. raise Exception(f"{llvm_dir} doesn't exist")
  81. build_dir = llvm_dir.joinpath(
  82. "win32build" if "windows" == platform else "build"
  83. ).resolve()
  84. build_dir.mkdir(exist_ok=True)
  85. lib_llvm_core_library = build_dir.joinpath("lib/libLLVMCore.a").resolve()
  86. if lib_llvm_core_library.exists():
  87. print(f"Please remove {build_dir} manually and try again")
  88. return build_dir
  89. compile_options = " ".join(
  90. LLVM_COMPILE_OPTIONS
  91. + LLVM_LIBXML2_OPTION
  92. + LLVM_EXTRA_COMPILE_OPTIONS.get(
  93. platform, LLVM_EXTRA_COMPILE_OPTIONS["default"]
  94. )
  95. + LLVM_TARGETS_TO_BUILD
  96. + LLVM_PROJECTS_TO_BUILD
  97. + LLVM_INCLUDE_TOOLS_OPTION
  98. )
  99. CONFIG_CMD = f"cmake {compile_options} ../llvm"
  100. if "windows" == platform:
  101. if "mingw" in sysconfig.get_platform().lower():
  102. CONFIG_CMD += " -G'Unix Makefiles'"
  103. else:
  104. CONFIG_CMD += " -A x64"
  105. print(f"{CONFIG_CMD}")
  106. subprocess.check_call(shlex.split(CONFIG_CMD), cwd=build_dir)
  107. BUILD_CMD = f"cmake --build . --target package --parallel {os.cpu_count()}" + (
  108. " --config Release" if "windows" == platform else ""
  109. )
  110. subprocess.check_call(shlex.split(BUILD_CMD), cwd=build_dir)
  111. return build_dir
  112. def repackage_llvm(llvm_dir):
  113. build_dir = llvm_dir.joinpath("./build").resolve()
  114. packs = [f for f in build_dir.glob("LLVM-13*.tar.gz")]
  115. if len(packs) > 1:
  116. raise Exception("Find more than one LLVM-13*.tar.gz")
  117. if not packs:
  118. return
  119. llvm_package = packs[0].name
  120. # mv build/LLVM-13.0.0*.gz .
  121. shutil.move(str(build_dir.joinpath(llvm_package).resolve()), str(llvm_dir))
  122. # rm -r build
  123. shutil.rmtree(str(build_dir))
  124. # mkdir build
  125. build_dir.mkdir()
  126. # tar xf ./LLVM-13.0.0-*.tar.gz --strip-components=1 --directory=build
  127. CMD = f"tar xf {llvm_dir.joinpath(llvm_package).resolve()} --strip-components=1 --directory={build_dir}"
  128. subprocess.check_call(shlex.split(CMD), cwd=llvm_dir)
  129. def main():
  130. parser = argparse.ArgumentParser(description="build necessary LLVM libraries")
  131. parser.add_argument(
  132. "--platform",
  133. type=str,
  134. choices=["android", "arc", "darwin", "linux", "windows", "xtensa"],
  135. help="identify current platform",
  136. )
  137. parser.add_argument(
  138. "--arch",
  139. nargs="+",
  140. type=str,
  141. choices=[
  142. "AArch64",
  143. "ARC",
  144. "ARM",
  145. "Mips",
  146. "RISCV",
  147. "WebAssembly",
  148. "X86",
  149. "Xtensa",
  150. ],
  151. help="identify LLVM supported backends, separate by space, like '--arch ARM Mips X86'",
  152. )
  153. parser.add_argument(
  154. "--project",
  155. nargs="+",
  156. type=str,
  157. default="",
  158. choices=["clang", "lldb"],
  159. help="identify extra LLVM projects, separate by space, like '--project clang lldb'",
  160. )
  161. options = parser.parse_args()
  162. print(f"options={options}")
  163. # if the "platform" is not identified in the command line option,
  164. # detect it
  165. if not options.platform:
  166. if sys.platform.startswith("win32") or sys.platform.startswith("msys"):
  167. platform = "windows"
  168. elif sys.platform.startswith("darwin"):
  169. platform = "darwin"
  170. else:
  171. platform = "linux"
  172. else:
  173. platform = options.platform
  174. print(f"========== Build LLVM for {platform} ==========\n")
  175. llvm_repo_and_branch = {
  176. "arc": {
  177. "repo": "https://github.com/llvm/llvm-project.git",
  178. "branch": "release/13.x",
  179. },
  180. "xtensa": {
  181. "repo": "https://github.com/espressif/llvm-project.git",
  182. "branch": "xtensa_release_11.0.0",
  183. },
  184. "default": {
  185. "repo": "https://github.com/llvm/llvm-project.git",
  186. "branch": "release/13.x",
  187. },
  188. }
  189. # retrieve the real file
  190. current_file = pathlib.Path(__file__)
  191. if current_file.is_symlink():
  192. current_file = pathlib.Path(os.readlink(current_file))
  193. current_dir = current_file.parent.resolve()
  194. deps_dir = current_dir.joinpath("../core/deps").resolve()
  195. try:
  196. print(f"==================== CLONE LLVM ====================")
  197. llvm_info = llvm_repo_and_branch.get(platform, llvm_repo_and_branch["default"])
  198. llvm_dir = clone_llvm(deps_dir, llvm_info["repo"], llvm_info["branch"])
  199. print()
  200. print(f"==================== BUILD LLVM ====================")
  201. build_llvm(llvm_dir, platform, options.arch, options.project)
  202. print()
  203. print(f"==================== PACKAGE LLVM ====================")
  204. repackage_llvm(llvm_dir)
  205. print()
  206. return True
  207. except subprocess.CalledProcessError:
  208. return False
  209. if __name__ == "__main__":
  210. sys.exit(0 if main() else 1)