llvm-compilers-check 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621
  1. #!/usr/bin/python3
  2. ##===- utils/llvmbuild - Build the LLVM project ----------------*-python-*-===##
  3. #
  4. # The LLVM Compiler Infrastructure
  5. #
  6. # This file is distributed under the University of Illinois Open Source
  7. # License. See LICENSE.TXT for details.
  8. #
  9. ##===----------------------------------------------------------------------===##
  10. #
  11. # This script builds many different flavors of the LLVM ecosystem. It
  12. # will build LLVM, Clang and dragonegg as well as run tests on them.
  13. # This script is convenient to use to check builds and tests before
  14. # committing changes to the upstream repository
  15. #
  16. # A typical source setup uses three trees and looks like this:
  17. #
  18. # official
  19. # dragonegg
  20. # llvm
  21. # tools
  22. # clang
  23. # staging
  24. # dragonegg
  25. # llvm
  26. # tools
  27. # clang
  28. # commit
  29. # dragonegg
  30. # llvm
  31. # tools
  32. # clang
  33. #
  34. # In a typical workflow, the "official" tree always contains unchanged
  35. # sources from the main LLVM project repositories. The "staging" tree
  36. # is where local work is done. A set of changes resides there waiting
  37. # to be moved upstream. The "commit" tree is where changes from
  38. # "staging" make their way upstream. Individual incremental changes
  39. # from "staging" are applied to "commit" and committed upstream after
  40. # a successful build and test run. A successful build is one in which
  41. # testing results in no more failures than seen in the testing of the
  42. # "official" tree.
  43. #
  44. # A build may be invoked as such:
  45. #
  46. # llvmbuild --src=~/llvm/commit --src=~/llvm/staging --src=~/llvm/official
  47. # --build=debug --build=release --build=paranoid
  48. # --prefix=/home/greened/install --builddir=/home/greened/build
  49. #
  50. # This will build the LLVM ecosystem, including LLVM, Clangand
  51. # dragonegg, putting build results in ~/build and installing tools in
  52. # ~/install. llvm-compilers-check creates separate build and install
  53. # directories for each source/build flavor. In the above example,
  54. # llvmbuild will build debug, release and paranoid (debug+checks)
  55. # flavors from each source tree (official, staging and commit) for a
  56. # total of nine builds. All builds will be run in parallel.
  57. #
  58. # The user may control parallelism via the --jobs and --threads
  59. # switches. --jobs tells llvm-compilers-checl the maximum total
  60. # number of builds to activate in parallel. The user may think of it
  61. # as equivalent to the GNU make -j switch. --threads tells
  62. # llvm-compilers-check how many worker threads to use to accomplish
  63. # those builds. If --threads is less than --jobs, --threads workers
  64. # will be launched and each one will pick a source/flavor combination
  65. # to build. Then llvm-compilers-check will invoke GNU make with -j
  66. # (--jobs / --threads) to use up the remaining job capacity. Once a
  67. # worker is finished with a build, it will pick another combination
  68. # off the list and start building it.
  69. #
  70. ##===----------------------------------------------------------------------===##
  71. import optparse
  72. import os
  73. import sys
  74. import threading
  75. import queue
  76. import logging
  77. import traceback
  78. import subprocess
  79. import re
  80. # TODO: Use shutil.which when it is available (3.2 or later)
  81. def find_executable(executable, path=None):
  82. """Try to find 'executable' in the directories listed in 'path' (a
  83. string listing directories separated by 'os.pathsep'; defaults to
  84. os.environ['PATH']). Returns the complete filename or None if not
  85. found
  86. """
  87. if path is None:
  88. path = os.environ['PATH']
  89. paths = path.split(os.pathsep)
  90. extlist = ['']
  91. if os.name == 'os2':
  92. (base, ext) = os.path.splitext(executable)
  93. # executable files on OS/2 can have an arbitrary extension, but
  94. # .exe is automatically appended if no dot is present in the name
  95. if not ext:
  96. executable = executable + ".exe"
  97. elif sys.platform == 'win32':
  98. pathext = os.environ['PATHEXT'].lower().split(os.pathsep)
  99. (base, ext) = os.path.splitext(executable)
  100. if ext.lower() not in pathext:
  101. extlist = pathext
  102. for ext in extlist:
  103. execname = executable + ext
  104. if os.path.isfile(execname):
  105. return execname
  106. else:
  107. for p in paths:
  108. f = os.path.join(p, execname)
  109. if os.path.isfile(f):
  110. return f
  111. else:
  112. return None
  113. def is_executable(fpath):
  114. return os.path.exists(fpath) and os.access(fpath, os.X_OK)
  115. def add_options(parser):
  116. parser.add_option("-v", "--verbose", action="store_true",
  117. default=False,
  118. help=("Output informational messages"
  119. " [default: %default]"))
  120. parser.add_option("--src", action="append",
  121. help=("Top-level source directory [default: %default]"))
  122. parser.add_option("--build", action="append",
  123. help=("Build types to run [default: %default]"))
  124. parser.add_option("--cc", default=find_executable("cc"),
  125. help=("The C compiler to use [default: %default]"))
  126. parser.add_option("--cxx", default=find_executable("c++"),
  127. help=("The C++ compiler to use [default: %default]"))
  128. parser.add_option("--threads", default=4, type="int",
  129. help=("The number of worker threads to use "
  130. "[default: %default]"))
  131. parser.add_option("--jobs", "-j", default=8, type="int",
  132. help=("The number of simultaneous build jobs "
  133. "[default: %default]"))
  134. parser.add_option("--prefix",
  135. help=("Root install directory [default: %default]"))
  136. parser.add_option("--builddir",
  137. help=("Root build directory [default: %default]"))
  138. parser.add_option("--extra-llvm-config-flags", default="",
  139. help=("Extra flags to pass to llvm configure [default: %default]"))
  140. parser.add_option("--force-configure", default=False, action="store_true",
  141. help=("Force reconfigure of all components"))
  142. parser.add_option("--no-dragonegg", default=False, action="store_true",
  143. help=("Do not build dragonegg"))
  144. parser.add_option("--no-install", default=False, action="store_true",
  145. help=("Do not do installs"))
  146. parser.add_option("--keep-going", default=False, action="store_true",
  147. help=("Keep going after failures"))
  148. parser.add_option("--no-flavor-prefix", default=False, action="store_true",
  149. help=("Do not append the build flavor to the install path"))
  150. parser.add_option("--enable-werror", default=False, action="store_true",
  151. help=("Build with -Werror"))
  152. return
  153. def check_options(parser, options, valid_builds):
  154. # See if we're building valid flavors.
  155. for build in options.build:
  156. if (build not in valid_builds):
  157. parser.error("'" + build + "' is not a valid build flavor "
  158. + str(valid_builds))
  159. # See if we can find source directories.
  160. for src in options.src:
  161. for component in components:
  162. component = component.rstrip("2")
  163. compsrc = src + "/" + component
  164. if (not os.path.isdir(compsrc)):
  165. parser.error("'" + compsrc + "' does not exist")
  166. # See if we can find the compilers
  167. options.cc = find_executable(options.cc)
  168. options.cxx = find_executable(options.cxx)
  169. return
  170. # Find a unique short name for the given set of paths. This searches
  171. # back through path components until it finds unique component names
  172. # among all given paths.
  173. def get_path_abbrevs(paths):
  174. # Find the number of common starting characters in the last component
  175. # of the paths.
  176. unique_paths = list(paths)
  177. class NotFoundException(Exception): pass
  178. # Find a unique component of each path.
  179. unique_bases = unique_paths[:]
  180. found = 0
  181. while len(unique_paths) > 0:
  182. bases = [os.path.basename(src) for src in unique_paths]
  183. components = { c for c in bases }
  184. # Account for single entry in paths.
  185. if len(components) > 1 or len(components) == len(bases):
  186. # We found something unique.
  187. for c in components:
  188. if bases.count(c) == 1:
  189. index = bases.index(c)
  190. unique_bases[index] = c
  191. # Remove the corresponding path from the set under
  192. # consideration.
  193. unique_paths[index] = None
  194. unique_paths = [ p for p in unique_paths if p is not None ]
  195. unique_paths = [os.path.dirname(src) for src in unique_paths]
  196. if len(unique_paths) > 0:
  197. raise NotFoundException()
  198. abbrevs = dict(zip(paths, [base for base in unique_bases]))
  199. return abbrevs
  200. # Given a set of unique names, find a short character sequence that
  201. # uniquely identifies them.
  202. def get_short_abbrevs(unique_bases):
  203. # Find a unique start character for each path base.
  204. my_unique_bases = unique_bases[:]
  205. unique_char_starts = unique_bases[:]
  206. while len(my_unique_bases) > 0:
  207. for start, char_tuple in enumerate(zip(*[base
  208. for base in my_unique_bases])):
  209. chars = { c for c in char_tuple }
  210. # Account for single path.
  211. if len(chars) > 1 or len(chars) == len(char_tuple):
  212. # We found something unique.
  213. for c in chars:
  214. if char_tuple.count(c) == 1:
  215. index = char_tuple.index(c)
  216. unique_char_starts[index] = start
  217. # Remove the corresponding path from the set under
  218. # consideration.
  219. my_unique_bases[index] = None
  220. my_unique_bases = [ b for b in my_unique_bases
  221. if b is not None ]
  222. break
  223. if len(my_unique_bases) > 0:
  224. raise NotFoundException()
  225. abbrevs = [abbrev[start_index:start_index+3]
  226. for abbrev, start_index
  227. in zip([base for base in unique_bases],
  228. [index for index in unique_char_starts])]
  229. abbrevs = dict(zip(unique_bases, abbrevs))
  230. return abbrevs
  231. class Builder(threading.Thread):
  232. class ExecutableNotFound(Exception): pass
  233. class FileNotExecutable(Exception): pass
  234. def __init__(self, work_queue, jobs,
  235. build_abbrev, source_abbrev,
  236. options):
  237. super().__init__()
  238. self.work_queue = work_queue
  239. self.jobs = jobs
  240. self.cc = options.cc
  241. self.cxx = options.cxx
  242. self.build_abbrev = build_abbrev
  243. self.source_abbrev = source_abbrev
  244. self.build_prefix = options.builddir
  245. self.install_prefix = options.prefix
  246. self.options = options
  247. self.component_abbrev = dict(
  248. llvm="llvm",
  249. dragonegg="degg")
  250. def run(self):
  251. while True:
  252. try:
  253. source, build = self.work_queue.get()
  254. self.dobuild(source, build)
  255. except:
  256. traceback.print_exc()
  257. finally:
  258. self.work_queue.task_done()
  259. def execute(self, command, execdir, env, component):
  260. prefix = self.component_abbrev[component.replace("-", "_")]
  261. pwd = os.getcwd()
  262. if not os.path.exists(execdir):
  263. os.makedirs(execdir)
  264. execenv = os.environ.copy()
  265. for key, value in env.items():
  266. execenv[key] = value
  267. self.logger.debug("[" + prefix + "] " + "env " + str(env) + " "
  268. + " ".join(command));
  269. try:
  270. proc = subprocess.Popen(command,
  271. cwd=execdir,
  272. env=execenv,
  273. stdout=subprocess.PIPE,
  274. stderr=subprocess.STDOUT)
  275. line = proc.stdout.readline()
  276. while line:
  277. self.logger.info("[" + prefix + "] "
  278. + str(line, "utf-8").rstrip())
  279. line = proc.stdout.readline()
  280. (stdoutdata, stderrdata) = proc.communicate()
  281. retcode = proc.wait()
  282. return retcode
  283. except:
  284. traceback.print_exc()
  285. # Get a list of C++ include directories to pass to clang.
  286. def get_includes(self):
  287. # Assume we're building with g++ for now.
  288. command = [self.cxx]
  289. command += ["-v", "-x", "c++", "/dev/null", "-fsyntax-only"]
  290. includes = []
  291. self.logger.debug(command)
  292. try:
  293. proc = subprocess.Popen(command,
  294. stdout=subprocess.PIPE,
  295. stderr=subprocess.STDOUT)
  296. gather = False
  297. line = proc.stdout.readline()
  298. while line:
  299. self.logger.debug(line)
  300. if re.search("End of search list", str(line)) is not None:
  301. self.logger.debug("Stop Gather")
  302. gather = False
  303. if gather:
  304. includes.append(str(line, "utf-8").strip())
  305. if re.search("#include <...> search starts", str(line)) is not None:
  306. self.logger.debug("Start Gather")
  307. gather = True
  308. line = proc.stdout.readline()
  309. except:
  310. traceback.print_exc()
  311. self.logger.debug(includes)
  312. return includes
  313. def dobuild(self, source, build):
  314. build_suffix = ""
  315. ssabbrev = get_short_abbrevs([ab for ab in self.source_abbrev.values()])
  316. prefix = "[" + ssabbrev[self.source_abbrev[source]] + "-" + self.build_abbrev[build] + "]"
  317. if (not self.options.no_flavor_prefix):
  318. self.install_prefix += "/" + self.source_abbrev[source] + "/" + build
  319. build_suffix += "/" + self.source_abbrev[source] + "/" + build
  320. self.logger = logging.getLogger(prefix)
  321. self.logger.debug(self.install_prefix)
  322. # Assume we're building with gcc for now.
  323. cxxincludes = self.get_includes()
  324. cxxroot = os.path.dirname(cxxincludes[0]) # Remove the version
  325. cxxroot = os.path.dirname(cxxroot) # Remove the c++
  326. cxxroot = os.path.dirname(cxxroot) # Remove the include
  327. configure_flags = dict(
  328. llvm=dict(debug=["--prefix=" + self.install_prefix,
  329. "--enable-assertions",
  330. "--disable-optimized",
  331. "--with-gcc-toolchain=" + cxxroot],
  332. release=["--prefix=" + self.install_prefix,
  333. "--enable-optimized",
  334. "--with-gcc-toolchain=" + cxxroot],
  335. paranoid=["--prefix=" + self.install_prefix,
  336. "--enable-assertions",
  337. "--enable-expensive-checks",
  338. "--disable-optimized",
  339. "--with-gcc-toolchain=" + cxxroot]),
  340. dragonegg=dict(debug=[],
  341. release=[],
  342. paranoid=[]))
  343. if (self.options.enable_werror):
  344. configure_flags["llvm"]["debug"].append("--enable-werror")
  345. configure_flags["llvm"]["release"].append("--enable-werror")
  346. configure_flags["llvm"]["paranoid"].append("--enable-werror")
  347. configure_env = dict(
  348. llvm=dict(debug=dict(CC=self.cc,
  349. CXX=self.cxx),
  350. release=dict(CC=self.cc,
  351. CXX=self.cxx),
  352. paranoid=dict(CC=self.cc,
  353. CXX=self.cxx)),
  354. dragonegg=dict(debug=dict(CC=self.cc,
  355. CXX=self.cxx),
  356. release=dict(CC=self.cc,
  357. CXX=self.cxx),
  358. paranoid=dict(CC=self.cc,
  359. CXX=self.cxx)))
  360. make_flags = dict(
  361. llvm=dict(debug=["-j" + str(self.jobs)],
  362. release=["-j" + str(self.jobs)],
  363. paranoid=["-j" + str(self.jobs)]),
  364. dragonegg=dict(debug=["-j" + str(self.jobs)],
  365. release=["-j" + str(self.jobs)],
  366. paranoid=["-j" + str(self.jobs)]))
  367. make_env = dict(
  368. llvm=dict(debug=dict(),
  369. release=dict(),
  370. paranoid=dict()),
  371. dragonegg=dict(debug=dict(GCC=self.cc,
  372. LLVM_CONFIG=self.install_prefix + "/bin/llvm-config"),
  373. release=dict(GCC=self.cc,
  374. LLVM_CONFIG=self.install_prefix + "/bin/llvm-config"),
  375. paranoid=dict(GCC=self.cc,
  376. LLVM_CONFIG=self.install_prefix + "/bin/llvm-config")))
  377. make_install_flags = dict(
  378. llvm=dict(debug=["install"],
  379. release=["install"],
  380. paranoid=["install"]),
  381. dragonegg=dict(debug=["install"],
  382. release=["install"],
  383. paranoid=["install"]))
  384. make_install_env = dict(
  385. llvm=dict(debug=dict(),
  386. release=dict(),
  387. paranoid=dict()),
  388. dragonegg=dict(debug=dict(),
  389. release=dict(),
  390. paranoid=dict()))
  391. make_check_flags = dict(
  392. llvm=dict(debug=["check"],
  393. release=["check"],
  394. paranoid=["check"]),
  395. dragonegg=dict(debug=["check"],
  396. release=["check"],
  397. paranoid=["check"]))
  398. make_check_env = dict(
  399. llvm=dict(debug=dict(),
  400. release=dict(),
  401. paranoid=dict()),
  402. dragonegg=dict(debug=dict(),
  403. release=dict(),
  404. paranoid=dict()))
  405. for component in components:
  406. comp = component[:]
  407. if (self.options.no_dragonegg):
  408. if (comp == 'dragonegg'):
  409. self.logger.info("Skipping " + component + " in "
  410. + builddir)
  411. continue
  412. srcdir = source + "/" + comp.rstrip("2")
  413. builddir = self.build_prefix + "/" + comp + "/" + build_suffix
  414. installdir = self.install_prefix
  415. comp_key = comp.replace("-", "_")
  416. config_args = configure_flags[comp_key][build][:]
  417. config_args.extend(getattr(self.options,
  418. "extra_" + comp_key.rstrip("2")
  419. + "_config_flags",
  420. "").split())
  421. self.logger.info("Configuring " + component + " in " + builddir)
  422. configrc = self.configure(component, srcdir, builddir,
  423. config_args,
  424. configure_env[comp_key][build])
  425. if (configrc == None) :
  426. self.logger.info("[None] Failed to configure " + component + " in " + installdir)
  427. if (configrc == 0 or self.options.keep_going) :
  428. self.logger.info("Building " + component + " in " + builddir)
  429. self.logger.info("Build: make " + str(make_flags[comp_key][build]))
  430. buildrc = self.make(component, srcdir, builddir,
  431. make_flags[comp_key][build],
  432. make_env[comp_key][build])
  433. if (buildrc == None) :
  434. self.logger.info("[None] Failed to build " + component + " in " + installdir)
  435. if (buildrc == 0 or self.options.keep_going) :
  436. self.logger.info("Testing " + component + " in " + builddir)
  437. self.logger.info("Test: make "
  438. + str(make_check_flags[comp_key][build]))
  439. testrc = self.make(component, srcdir, builddir,
  440. make_check_flags[comp_key][build],
  441. make_check_env[comp_key][build])
  442. if (testrc == None) :
  443. self.logger.info("[None] Failed to test " + component + " in " + installdir)
  444. if ((testrc == 0 or self.options.keep_going)
  445. and not self.options.no_install):
  446. self.logger.info("Installing " + component + " in " + installdir)
  447. self.make(component, srcdir, builddir,
  448. make_install_flags[comp_key][build],
  449. make_install_env[comp_key][build])
  450. else :
  451. self.logger.info("Failed testing " + component + " in " + installdir)
  452. else :
  453. self.logger.info("Failed to build " + component + " in " + installdir)
  454. else :
  455. self.logger.info("Failed to configure " + component + " in " + installdir)
  456. def configure(self, component, srcdir, builddir, flags, env):
  457. prefix = self.component_abbrev[component.replace("-", "_")]
  458. self.logger.debug("Configure " + str(flags) + " " + str(srcdir) + " -> "
  459. + str(builddir))
  460. configure_files = dict(
  461. llvm=[(srcdir + "/configure", builddir + "/Makefile")],
  462. dragonegg=[(None,None)])
  463. doconfig = False
  464. for conf, mf in configure_files[component.replace("-", "_")]:
  465. if conf is None:
  466. # No configure necessary
  467. return 0
  468. if not os.path.exists(conf):
  469. self.logger.info("[" + prefix + "] Configure failed, no configure script " + conf)
  470. return -1
  471. if os.path.exists(conf) and os.path.exists(mf):
  472. confstat = os.stat(conf)
  473. makestat = os.stat(mf)
  474. if confstat.st_mtime > makestat.st_mtime:
  475. doconfig = True
  476. break
  477. else:
  478. doconfig = True
  479. break
  480. if not doconfig and not self.options.force_configure:
  481. return 0
  482. program = srcdir + "/configure"
  483. if not is_executable(program):
  484. self.logger.info("[" + prefix + "] Configure failed, cannot execute " + program)
  485. return -1
  486. args = [program]
  487. args += ["--verbose"]
  488. args += flags
  489. return self.execute(args, builddir, env, component)
  490. def make(self, component, srcdir, builddir, flags, env):
  491. program = find_executable("make")
  492. if program is None:
  493. raise ExecutableNotFound
  494. if not is_executable(program):
  495. raise FileNotExecutable
  496. args = [program]
  497. args += flags
  498. return self.execute(args, builddir, env, component)
  499. # Global constants
  500. build_abbrev = dict(debug="dbg", release="opt", paranoid="par")
  501. components = ["llvm", "dragonegg"]
  502. # Parse options
  503. parser = optparse.OptionParser(version="%prog 1.0")
  504. add_options(parser)
  505. (options, args) = parser.parse_args()
  506. check_options(parser, options, build_abbrev.keys());
  507. if options.verbose:
  508. logging.basicConfig(level=logging.DEBUG,
  509. format='%(name)-13s: %(message)s')
  510. else:
  511. logging.basicConfig(level=logging.INFO,
  512. format='%(name)-13s: %(message)s')
  513. source_abbrev = get_path_abbrevs(set(options.src))
  514. work_queue = queue.Queue()
  515. jobs = options.jobs // options.threads
  516. if jobs == 0:
  517. jobs = 1
  518. numthreads = options.threads
  519. logging.getLogger().info("Building with " + str(options.jobs) + " jobs and "
  520. + str(numthreads) + " threads using " + str(jobs)
  521. + " make jobs")
  522. logging.getLogger().info("CC = " + str(options.cc))
  523. logging.getLogger().info("CXX = " + str(options.cxx))
  524. for t in range(numthreads):
  525. builder = Builder(work_queue, jobs,
  526. build_abbrev, source_abbrev,
  527. options)
  528. builder.daemon = True
  529. builder.start()
  530. for build in set(options.build):
  531. for source in set(options.src):
  532. work_queue.put((source, build))
  533. work_queue.join()