1*a54f160bSHarmen Stoppels#!/usr/bin/env python3
29ef81066SDaniel Dunbar##===- utils/llvmbuild - Build the LLVM project ----------------*-python-*-===##
39ef81066SDaniel Dunbar#
42946cd70SChandler Carruth# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
52946cd70SChandler Carruth# See https://llvm.org/LICENSE.txt for license information.
62946cd70SChandler Carruth# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
79ef81066SDaniel Dunbar#
89ef81066SDaniel Dunbar##===----------------------------------------------------------------------===##
99ef81066SDaniel Dunbar#
109ef81066SDaniel Dunbar# This script builds many different flavors of the LLVM ecosystem.  It
11c26e5fb8SDavid Greene# will build LLVM, Clang and dragonegg as well as run tests on them.
12c26e5fb8SDavid Greene# This script is convenient to use to check builds and tests before
13c26e5fb8SDavid Greene# committing changes to the upstream repository
149ef81066SDaniel Dunbar#
159ef81066SDaniel Dunbar# A typical source setup uses three trees and looks like this:
169ef81066SDaniel Dunbar#
179ef81066SDaniel Dunbar# official
189ef81066SDaniel Dunbar#   dragonegg
199ef81066SDaniel Dunbar#   llvm
209ef81066SDaniel Dunbar#     tools
219ef81066SDaniel Dunbar#       clang
229ef81066SDaniel Dunbar# staging
239ef81066SDaniel Dunbar#   dragonegg
249ef81066SDaniel Dunbar#   llvm
259ef81066SDaniel Dunbar#     tools
269ef81066SDaniel Dunbar#       clang
279ef81066SDaniel Dunbar# commit
289ef81066SDaniel Dunbar#   dragonegg
299ef81066SDaniel Dunbar#   llvm
309ef81066SDaniel Dunbar#     tools
319ef81066SDaniel Dunbar#       clang
329ef81066SDaniel Dunbar#
339ef81066SDaniel Dunbar# In a typical workflow, the "official" tree always contains unchanged
349ef81066SDaniel Dunbar# sources from the main LLVM project repositories.  The "staging" tree
359ef81066SDaniel Dunbar# is where local work is done.  A set of changes resides there waiting
369ef81066SDaniel Dunbar# to be moved upstream.  The "commit" tree is where changes from
379ef81066SDaniel Dunbar# "staging" make their way upstream.  Individual incremental changes
389ef81066SDaniel Dunbar# from "staging" are applied to "commit" and committed upstream after
399ef81066SDaniel Dunbar# a successful build and test run.  A successful build is one in which
409ef81066SDaniel Dunbar# testing results in no more failures than seen in the testing of the
419ef81066SDaniel Dunbar# "official" tree.
429ef81066SDaniel Dunbar#
439ef81066SDaniel Dunbar# A build may be invoked as such:
449ef81066SDaniel Dunbar#
45c26e5fb8SDavid Greene# llvmbuild --src=~/llvm/commit --src=~/llvm/staging --src=~/llvm/official
469ef81066SDaniel Dunbar#   --build=debug --build=release --build=paranoid
479ef81066SDaniel Dunbar#   --prefix=/home/greened/install --builddir=/home/greened/build
489ef81066SDaniel Dunbar#
49c26e5fb8SDavid Greene# This will build the LLVM ecosystem, including LLVM, Clangand
50c26e5fb8SDavid Greene# dragonegg, putting build results in ~/build and installing tools in
51c26e5fb8SDavid Greene# ~/install.  llvm-compilers-check creates separate build and install
52c26e5fb8SDavid Greene# directories for each source/build flavor.  In the above example,
53c26e5fb8SDavid Greene# llvmbuild will build debug, release and paranoid (debug+checks)
54c26e5fb8SDavid Greene# flavors from each source tree (official, staging and commit) for a
55c26e5fb8SDavid Greene# total of nine builds.  All builds will be run in parallel.
569ef81066SDaniel Dunbar#
579ef81066SDaniel Dunbar# The user may control parallelism via the --jobs and --threads
58c100d7baSDavid Green# switches.  --jobs tells llvm-compilers-check the maximum total
59c26e5fb8SDavid Greene# number of builds to activate in parallel.  The user may think of it
60c26e5fb8SDavid Greene# as equivalent to the GNU make -j switch.  --threads tells
61c26e5fb8SDavid Greene# llvm-compilers-check how many worker threads to use to accomplish
62c26e5fb8SDavid Greene# those builds.  If --threads is less than --jobs, --threads workers
63c26e5fb8SDavid Greene# will be launched and each one will pick a source/flavor combination
64c26e5fb8SDavid Greene# to build.  Then llvm-compilers-check will invoke GNU make with -j
65c26e5fb8SDavid Greene# (--jobs / --threads) to use up the remaining job capacity.  Once a
66c26e5fb8SDavid Greene# worker is finished with a build, it will pick another combination
67c26e5fb8SDavid Greene# off the list and start building it.
689ef81066SDaniel Dunbar#
699ef81066SDaniel Dunbar##===----------------------------------------------------------------------===##
709ef81066SDaniel Dunbar
719ef81066SDaniel Dunbarimport optparse
729ef81066SDaniel Dunbarimport os
739ef81066SDaniel Dunbarimport sys
749ef81066SDaniel Dunbarimport threading
759ef81066SDaniel Dunbarimport queue
769ef81066SDaniel Dunbarimport logging
779ef81066SDaniel Dunbarimport traceback
789ef81066SDaniel Dunbarimport subprocess
799ef81066SDaniel Dunbarimport re
809ef81066SDaniel Dunbar
819ef81066SDaniel Dunbar# TODO: Use shutil.which when it is available (3.2 or later)
829ef81066SDaniel Dunbardef find_executable(executable, path=None):
839ef81066SDaniel Dunbar    """Try to find 'executable' in the directories listed in 'path' (a
849ef81066SDaniel Dunbar    string listing directories separated by 'os.pathsep'; defaults to
859ef81066SDaniel Dunbar    os.environ['PATH']).  Returns the complete filename or None if not
869ef81066SDaniel Dunbar    found
879ef81066SDaniel Dunbar    """
889ef81066SDaniel Dunbar    if path is None:
899ef81066SDaniel Dunbar        path = os.environ['PATH']
909ef81066SDaniel Dunbar    paths = path.split(os.pathsep)
919ef81066SDaniel Dunbar    extlist = ['']
929ef81066SDaniel Dunbar    if os.name == 'os2':
939ef81066SDaniel Dunbar        (base, ext) = os.path.splitext(executable)
949ef81066SDaniel Dunbar        # executable files on OS/2 can have an arbitrary extension, but
959ef81066SDaniel Dunbar        # .exe is automatically appended if no dot is present in the name
969ef81066SDaniel Dunbar        if not ext:
979ef81066SDaniel Dunbar            executable = executable + ".exe"
989ef81066SDaniel Dunbar    elif sys.platform == 'win32':
999ef81066SDaniel Dunbar        pathext = os.environ['PATHEXT'].lower().split(os.pathsep)
1009ef81066SDaniel Dunbar        (base, ext) = os.path.splitext(executable)
1019ef81066SDaniel Dunbar        if ext.lower() not in pathext:
1029ef81066SDaniel Dunbar            extlist = pathext
1039ef81066SDaniel Dunbar    for ext in extlist:
1049ef81066SDaniel Dunbar        execname = executable + ext
1059ef81066SDaniel Dunbar        if os.path.isfile(execname):
1069ef81066SDaniel Dunbar            return execname
1079ef81066SDaniel Dunbar        else:
1089ef81066SDaniel Dunbar            for p in paths:
1099ef81066SDaniel Dunbar                f = os.path.join(p, execname)
1109ef81066SDaniel Dunbar                if os.path.isfile(f):
1119ef81066SDaniel Dunbar                    return f
1129ef81066SDaniel Dunbar    else:
1139ef81066SDaniel Dunbar        return None
1149ef81066SDaniel Dunbar
1159ef81066SDaniel Dunbardef is_executable(fpath):
1169ef81066SDaniel Dunbar    return os.path.exists(fpath) and os.access(fpath, os.X_OK)
1179ef81066SDaniel Dunbar
1189ef81066SDaniel Dunbardef add_options(parser):
1199ef81066SDaniel Dunbar    parser.add_option("-v", "--verbose", action="store_true",
1209ef81066SDaniel Dunbar                      default=False,
1219ef81066SDaniel Dunbar                      help=("Output informational messages"
1229ef81066SDaniel Dunbar                            " [default: %default]"))
1239ef81066SDaniel Dunbar    parser.add_option("--src", action="append",
1249ef81066SDaniel Dunbar                      help=("Top-level source directory [default: %default]"))
1259ef81066SDaniel Dunbar    parser.add_option("--build", action="append",
1269ef81066SDaniel Dunbar                      help=("Build types to run [default: %default]"))
1279ef81066SDaniel Dunbar    parser.add_option("--cc", default=find_executable("cc"),
1289ef81066SDaniel Dunbar                      help=("The C compiler to use [default: %default]"))
1299ef81066SDaniel Dunbar    parser.add_option("--cxx", default=find_executable("c++"),
1309ef81066SDaniel Dunbar                      help=("The C++ compiler to use [default: %default]"))
1319ef81066SDaniel Dunbar    parser.add_option("--threads", default=4, type="int",
1329ef81066SDaniel Dunbar                      help=("The number of worker threads to use "
1339ef81066SDaniel Dunbar                            "[default: %default]"))
1349ef81066SDaniel Dunbar    parser.add_option("--jobs", "-j", default=8, type="int",
1359ef81066SDaniel Dunbar                      help=("The number of simultaneous build jobs "
1369ef81066SDaniel Dunbar                            "[default: %default]"))
1379ef81066SDaniel Dunbar    parser.add_option("--prefix",
1389ef81066SDaniel Dunbar                      help=("Root install directory [default: %default]"))
1399ef81066SDaniel Dunbar    parser.add_option("--builddir",
1409ef81066SDaniel Dunbar                      help=("Root build directory [default: %default]"))
1419ef81066SDaniel Dunbar    parser.add_option("--extra-llvm-config-flags", default="",
1429ef81066SDaniel Dunbar                      help=("Extra flags to pass to llvm configure [default: %default]"))
1439ef81066SDaniel Dunbar    parser.add_option("--force-configure", default=False, action="store_true",
1449ef81066SDaniel Dunbar                      help=("Force reconfigure of all components"))
145c26e5fb8SDavid Greene    parser.add_option("--no-dragonegg", default=False, action="store_true",
146c26e5fb8SDavid Greene                      help=("Do not build dragonegg"))
1479ef81066SDaniel Dunbar    parser.add_option("--no-install", default=False, action="store_true",
1489ef81066SDaniel Dunbar                      help=("Do not do installs"))
149a15b16f2SDavid Greene    parser.add_option("--keep-going", default=False, action="store_true",
150a15b16f2SDavid Greene                      help=("Keep going after failures"))
15163677389SDavid Greene    parser.add_option("--no-flavor-prefix", default=False, action="store_true",
15263677389SDavid Greene                      help=("Do not append the build flavor to the install path"))
1539ccdb170SDavid Greene    parser.add_option("--enable-werror", default=False, action="store_true",
1549ccdb170SDavid Greene                      help=("Build with -Werror"))
1559ef81066SDaniel Dunbar    return
1569ef81066SDaniel Dunbar
1579ef81066SDaniel Dunbardef check_options(parser, options, valid_builds):
1589ef81066SDaniel Dunbar    # See if we're building valid flavors.
1599ef81066SDaniel Dunbar    for build in options.build:
1609ef81066SDaniel Dunbar        if (build not in valid_builds):
1619ef81066SDaniel Dunbar            parser.error("'" + build + "' is not a valid build flavor "
1629ef81066SDaniel Dunbar                         + str(valid_builds))
1639ef81066SDaniel Dunbar
1649ef81066SDaniel Dunbar    # See if we can find source directories.
1659ef81066SDaniel Dunbar    for src in options.src:
1669ef81066SDaniel Dunbar        for component in components:
1679ef81066SDaniel Dunbar            component = component.rstrip("2")
1689ef81066SDaniel Dunbar            compsrc = src + "/" + component
1699ef81066SDaniel Dunbar            if (not os.path.isdir(compsrc)):
1709ef81066SDaniel Dunbar                parser.error("'" + compsrc + "' does not exist")
1719ef81066SDaniel Dunbar
1729ef81066SDaniel Dunbar    # See if we can find the compilers
1739ef81066SDaniel Dunbar    options.cc = find_executable(options.cc)
1749ef81066SDaniel Dunbar    options.cxx = find_executable(options.cxx)
1759ef81066SDaniel Dunbar
1769ef81066SDaniel Dunbar    return
1779ef81066SDaniel Dunbar
1789ef81066SDaniel Dunbar# Find a unique short name for the given set of paths.  This searches
1799ef81066SDaniel Dunbar# back through path components until it finds unique component names
1809ef81066SDaniel Dunbar# among all given paths.
1819ef81066SDaniel Dunbardef get_path_abbrevs(paths):
1829ef81066SDaniel Dunbar    # Find the number of common starting characters in the last component
1839ef81066SDaniel Dunbar    # of the paths.
1849ef81066SDaniel Dunbar    unique_paths = list(paths)
1859ef81066SDaniel Dunbar
1869ef81066SDaniel Dunbar    class NotFoundException(Exception): pass
1879ef81066SDaniel Dunbar
1889ef81066SDaniel Dunbar    # Find a unique component of each path.
1899ef81066SDaniel Dunbar    unique_bases = unique_paths[:]
1909ef81066SDaniel Dunbar    found = 0
1919ef81066SDaniel Dunbar    while len(unique_paths) > 0:
1929ef81066SDaniel Dunbar        bases = [os.path.basename(src) for src in unique_paths]
1939ef81066SDaniel Dunbar        components = { c for c in bases }
1949ef81066SDaniel Dunbar        # Account for single entry in paths.
1959ef81066SDaniel Dunbar        if len(components) > 1 or len(components) == len(bases):
1969ef81066SDaniel Dunbar            # We found something unique.
1979ef81066SDaniel Dunbar            for c in components:
1989ef81066SDaniel Dunbar                if bases.count(c) == 1:
1999ef81066SDaniel Dunbar                   index = bases.index(c)
2009ef81066SDaniel Dunbar                   unique_bases[index] = c
2019ef81066SDaniel Dunbar                   # Remove the corresponding path from the set under
2029ef81066SDaniel Dunbar                   # consideration.
2039ef81066SDaniel Dunbar                   unique_paths[index] = None
2049ef81066SDaniel Dunbar            unique_paths = [ p for p in unique_paths if p is not None ]
2059ef81066SDaniel Dunbar        unique_paths = [os.path.dirname(src) for src in unique_paths]
2069ef81066SDaniel Dunbar
2079ef81066SDaniel Dunbar    if len(unique_paths) > 0:
2089ef81066SDaniel Dunbar        raise NotFoundException()
2099ef81066SDaniel Dunbar
2109ef81066SDaniel Dunbar    abbrevs = dict(zip(paths, [base for base in unique_bases]))
2119ef81066SDaniel Dunbar
2129ef81066SDaniel Dunbar    return abbrevs
2139ef81066SDaniel Dunbar
2149ef81066SDaniel Dunbar# Given a set of unique names, find a short character sequence that
2159ef81066SDaniel Dunbar# uniquely identifies them.
2169ef81066SDaniel Dunbardef get_short_abbrevs(unique_bases):
2179ef81066SDaniel Dunbar    # Find a unique start character for each path base.
2189ef81066SDaniel Dunbar    my_unique_bases = unique_bases[:]
2199ef81066SDaniel Dunbar    unique_char_starts = unique_bases[:]
2209ef81066SDaniel Dunbar    while len(my_unique_bases) > 0:
2219ef81066SDaniel Dunbar        for start, char_tuple in enumerate(zip(*[base
2229ef81066SDaniel Dunbar                                                 for base in my_unique_bases])):
2239ef81066SDaniel Dunbar            chars = { c for c in char_tuple }
2249ef81066SDaniel Dunbar            # Account for single path.
2259ef81066SDaniel Dunbar            if len(chars) > 1 or len(chars) == len(char_tuple):
2269ef81066SDaniel Dunbar                # We found something unique.
2279ef81066SDaniel Dunbar                for c in chars:
2289ef81066SDaniel Dunbar                    if char_tuple.count(c) == 1:
2299ef81066SDaniel Dunbar                        index = char_tuple.index(c)
2309ef81066SDaniel Dunbar                        unique_char_starts[index] = start
2319ef81066SDaniel Dunbar                        # Remove the corresponding path from the set under
2329ef81066SDaniel Dunbar                        # consideration.
2339ef81066SDaniel Dunbar                        my_unique_bases[index] = None
2349ef81066SDaniel Dunbar                my_unique_bases = [ b for b in my_unique_bases
2359ef81066SDaniel Dunbar                                    if b is not None ]
2369ef81066SDaniel Dunbar                break
2379ef81066SDaniel Dunbar
2389ef81066SDaniel Dunbar    if len(my_unique_bases) > 0:
2399ef81066SDaniel Dunbar        raise NotFoundException()
2409ef81066SDaniel Dunbar
2419ef81066SDaniel Dunbar    abbrevs = [abbrev[start_index:start_index+3]
2429ef81066SDaniel Dunbar               for abbrev, start_index
2439ef81066SDaniel Dunbar               in zip([base for base in unique_bases],
2449ef81066SDaniel Dunbar                      [index for index in unique_char_starts])]
2459ef81066SDaniel Dunbar
2469ef81066SDaniel Dunbar    abbrevs = dict(zip(unique_bases, abbrevs))
2479ef81066SDaniel Dunbar
2489ef81066SDaniel Dunbar    return abbrevs
2499ef81066SDaniel Dunbar
2509ef81066SDaniel Dunbarclass Builder(threading.Thread):
2519ef81066SDaniel Dunbar    class ExecutableNotFound(Exception): pass
2529ef81066SDaniel Dunbar    class FileNotExecutable(Exception): pass
2539ef81066SDaniel Dunbar
2549ef81066SDaniel Dunbar    def __init__(self, work_queue, jobs,
255c26e5fb8SDavid Greene                 build_abbrev, source_abbrev,
2569ef81066SDaniel Dunbar                 options):
2579ef81066SDaniel Dunbar        super().__init__()
2589ef81066SDaniel Dunbar        self.work_queue = work_queue
2599ef81066SDaniel Dunbar        self.jobs = jobs
2609ef81066SDaniel Dunbar        self.cc = options.cc
2619ef81066SDaniel Dunbar        self.cxx = options.cxx
2629ef81066SDaniel Dunbar        self.build_abbrev = build_abbrev
2639ef81066SDaniel Dunbar        self.source_abbrev = source_abbrev
2649ef81066SDaniel Dunbar        self.build_prefix = options.builddir
2659ef81066SDaniel Dunbar        self.install_prefix = options.prefix
2669ef81066SDaniel Dunbar        self.options = options
2679ef81066SDaniel Dunbar        self.component_abbrev = dict(
2689ef81066SDaniel Dunbar            llvm="llvm",
269c26e5fb8SDavid Greene            dragonegg="degg")
2709ef81066SDaniel Dunbar    def run(self):
2719ef81066SDaniel Dunbar        while True:
2729ef81066SDaniel Dunbar            try:
273c26e5fb8SDavid Greene                source, build = self.work_queue.get()
274c26e5fb8SDavid Greene                self.dobuild(source, build)
2759ef81066SDaniel Dunbar            except:
2769ef81066SDaniel Dunbar                traceback.print_exc()
2779ef81066SDaniel Dunbar            finally:
2789ef81066SDaniel Dunbar                self.work_queue.task_done()
2799ef81066SDaniel Dunbar
2809ef81066SDaniel Dunbar    def execute(self, command, execdir, env, component):
2819ef81066SDaniel Dunbar        prefix = self.component_abbrev[component.replace("-", "_")]
2829ef81066SDaniel Dunbar        pwd = os.getcwd()
2839ef81066SDaniel Dunbar        if not os.path.exists(execdir):
2849ef81066SDaniel Dunbar            os.makedirs(execdir)
2859ef81066SDaniel Dunbar
2869ef81066SDaniel Dunbar        execenv = os.environ.copy()
2879ef81066SDaniel Dunbar
2889ef81066SDaniel Dunbar        for key, value in env.items():
2899ef81066SDaniel Dunbar            execenv[key] = value
2909ef81066SDaniel Dunbar
2919ef81066SDaniel Dunbar        self.logger.debug("[" + prefix + "] " + "env " + str(env) + " "
2929ef81066SDaniel Dunbar                          + " ".join(command));
2939ef81066SDaniel Dunbar
2949ef81066SDaniel Dunbar        try:
2959ef81066SDaniel Dunbar            proc = subprocess.Popen(command,
2969ef81066SDaniel Dunbar                                    cwd=execdir,
2979ef81066SDaniel Dunbar                                    env=execenv,
2989ef81066SDaniel Dunbar                                    stdout=subprocess.PIPE,
2999ef81066SDaniel Dunbar                                    stderr=subprocess.STDOUT)
3009ef81066SDaniel Dunbar
3019ef81066SDaniel Dunbar            line = proc.stdout.readline()
3029ef81066SDaniel Dunbar            while line:
3039ef81066SDaniel Dunbar                self.logger.info("[" + prefix + "] "
3049ef81066SDaniel Dunbar                                 + str(line, "utf-8").rstrip())
3059ef81066SDaniel Dunbar                line = proc.stdout.readline()
3069ef81066SDaniel Dunbar
307a15b16f2SDavid Greene            (stdoutdata, stderrdata) = proc.communicate()
308a15b16f2SDavid Greene            retcode = proc.wait()
309a15b16f2SDavid Greene
310a15b16f2SDavid Greene            return retcode
311a15b16f2SDavid Greene
3129ef81066SDaniel Dunbar        except:
3139ef81066SDaniel Dunbar            traceback.print_exc()
3149ef81066SDaniel Dunbar
3159ef81066SDaniel Dunbar    # Get a list of C++ include directories to pass to clang.
3169ef81066SDaniel Dunbar    def get_includes(self):
3179ef81066SDaniel Dunbar        # Assume we're building with g++ for now.
3189ef81066SDaniel Dunbar        command = [self.cxx]
3199ef81066SDaniel Dunbar        command += ["-v", "-x", "c++", "/dev/null", "-fsyntax-only"]
3209ef81066SDaniel Dunbar        includes = []
3219ef81066SDaniel Dunbar        self.logger.debug(command)
3229ef81066SDaniel Dunbar        try:
3239ef81066SDaniel Dunbar            proc = subprocess.Popen(command,
3249ef81066SDaniel Dunbar                                    stdout=subprocess.PIPE,
3259ef81066SDaniel Dunbar                                    stderr=subprocess.STDOUT)
3269ef81066SDaniel Dunbar
3279ef81066SDaniel Dunbar            gather = False
3289ef81066SDaniel Dunbar            line = proc.stdout.readline()
3299ef81066SDaniel Dunbar            while line:
3309ef81066SDaniel Dunbar                self.logger.debug(line)
3319ef81066SDaniel Dunbar                if re.search("End of search list", str(line)) is not None:
3329ef81066SDaniel Dunbar                    self.logger.debug("Stop Gather")
3339ef81066SDaniel Dunbar                    gather = False
3349ef81066SDaniel Dunbar                if gather:
3359ef81066SDaniel Dunbar                    includes.append(str(line, "utf-8").strip())
3369ef81066SDaniel Dunbar                if re.search("#include <...> search starts", str(line)) is not None:
3379ef81066SDaniel Dunbar                    self.logger.debug("Start Gather")
3389ef81066SDaniel Dunbar                    gather = True
3399ef81066SDaniel Dunbar                line = proc.stdout.readline()
340a15b16f2SDavid Greene
3419ef81066SDaniel Dunbar        except:
3429ef81066SDaniel Dunbar            traceback.print_exc()
3439ef81066SDaniel Dunbar        self.logger.debug(includes)
3449ef81066SDaniel Dunbar        return includes
3459ef81066SDaniel Dunbar
346c26e5fb8SDavid Greene    def dobuild(self, source, build):
3479ef81066SDaniel Dunbar        build_suffix = ""
3489ef81066SDaniel Dunbar
3499ef81066SDaniel Dunbar        ssabbrev = get_short_abbrevs([ab for ab in self.source_abbrev.values()])
3509ef81066SDaniel Dunbar
3519ef81066SDaniel Dunbar        prefix = "[" + ssabbrev[self.source_abbrev[source]] + "-" + self.build_abbrev[build] + "]"
35263677389SDavid Greene        if (not self.options.no_flavor_prefix):
3539ef81066SDaniel Dunbar            self.install_prefix += "/" + self.source_abbrev[source] + "/" + build
35463677389SDavid Greene
3559ef81066SDaniel Dunbar        build_suffix += "/" + self.source_abbrev[source] + "/" + build
3569ef81066SDaniel Dunbar
3579ef81066SDaniel Dunbar        self.logger = logging.getLogger(prefix)
3589ef81066SDaniel Dunbar
3599ef81066SDaniel Dunbar        self.logger.debug(self.install_prefix)
3609ef81066SDaniel Dunbar
3619ef81066SDaniel Dunbar        # Assume we're building with gcc for now.
3629ef81066SDaniel Dunbar        cxxincludes = self.get_includes()
363ec217f6aSRafael Espindola        cxxroot = os.path.dirname(cxxincludes[0]) # Remove the version
364ec217f6aSRafael Espindola        cxxroot = os.path.dirname(cxxroot)        # Remove the c++
365ec217f6aSRafael Espindola        cxxroot = os.path.dirname(cxxroot)        # Remove the include
3669ef81066SDaniel Dunbar
3679ef81066SDaniel Dunbar        configure_flags = dict(
3689ef81066SDaniel Dunbar            llvm=dict(debug=["--prefix=" + self.install_prefix,
3699ef81066SDaniel Dunbar                             "--enable-assertions",
3709ef81066SDaniel Dunbar                             "--disable-optimized",
371ec217f6aSRafael Espindola                             "--with-gcc-toolchain=" + cxxroot],
3729ef81066SDaniel Dunbar                      release=["--prefix=" + self.install_prefix,
3739ef81066SDaniel Dunbar                               "--enable-optimized",
374ec217f6aSRafael Espindola                               "--with-gcc-toolchain=" + cxxroot],
3759ef81066SDaniel Dunbar                      paranoid=["--prefix=" + self.install_prefix,
3769ef81066SDaniel Dunbar                                "--enable-assertions",
3779ef81066SDaniel Dunbar                                "--enable-expensive-checks",
3789ef81066SDaniel Dunbar                                "--disable-optimized",
379ec217f6aSRafael Espindola                                "--with-gcc-toolchain=" + cxxroot]),
3809ef81066SDaniel Dunbar            dragonegg=dict(debug=[],
3819ef81066SDaniel Dunbar                           release=[],
3829ef81066SDaniel Dunbar                           paranoid=[]))
3839ef81066SDaniel Dunbar
3849ccdb170SDavid Greene        if (self.options.enable_werror):
3859ccdb170SDavid Greene            configure_flags["llvm"]["debug"].append("--enable-werror")
3869ccdb170SDavid Greene            configure_flags["llvm"]["release"].append("--enable-werror")
3879ccdb170SDavid Greene            configure_flags["llvm"]["paranoid"].append("--enable-werror")
3889ccdb170SDavid Greene
3899ef81066SDaniel Dunbar        configure_env = dict(
3909ef81066SDaniel Dunbar            llvm=dict(debug=dict(CC=self.cc,
3919ef81066SDaniel Dunbar                                 CXX=self.cxx),
3929ef81066SDaniel Dunbar                      release=dict(CC=self.cc,
3939ef81066SDaniel Dunbar                                   CXX=self.cxx),
3949ef81066SDaniel Dunbar                      paranoid=dict(CC=self.cc,
3959ef81066SDaniel Dunbar                                    CXX=self.cxx)),
3969ef81066SDaniel Dunbar            dragonegg=dict(debug=dict(CC=self.cc,
3979ef81066SDaniel Dunbar                                      CXX=self.cxx),
3989ef81066SDaniel Dunbar                           release=dict(CC=self.cc,
3999ef81066SDaniel Dunbar                                        CXX=self.cxx),
4009ef81066SDaniel Dunbar                           paranoid=dict(CC=self.cc,
4019ef81066SDaniel Dunbar                                         CXX=self.cxx)))
4029ef81066SDaniel Dunbar
4039ef81066SDaniel Dunbar        make_flags = dict(
4049ef81066SDaniel Dunbar            llvm=dict(debug=["-j" + str(self.jobs)],
4059ef81066SDaniel Dunbar                      release=["-j" + str(self.jobs)],
4069ef81066SDaniel Dunbar                      paranoid=["-j" + str(self.jobs)]),
4079ef81066SDaniel Dunbar            dragonegg=dict(debug=["-j" + str(self.jobs)],
4089ef81066SDaniel Dunbar                           release=["-j" + str(self.jobs)],
4099ef81066SDaniel Dunbar                           paranoid=["-j" + str(self.jobs)]))
4109ef81066SDaniel Dunbar
4119ef81066SDaniel Dunbar        make_env = dict(
4129ef81066SDaniel Dunbar            llvm=dict(debug=dict(),
4139ef81066SDaniel Dunbar                      release=dict(),
4149ef81066SDaniel Dunbar                      paranoid=dict()),
415c26e5fb8SDavid Greene            dragonegg=dict(debug=dict(GCC=self.cc,
4169ef81066SDaniel Dunbar                                      LLVM_CONFIG=self.install_prefix + "/bin/llvm-config"),
417c26e5fb8SDavid Greene                           release=dict(GCC=self.cc,
4189ef81066SDaniel Dunbar                                        LLVM_CONFIG=self.install_prefix + "/bin/llvm-config"),
419c26e5fb8SDavid Greene                           paranoid=dict(GCC=self.cc,
4209ef81066SDaniel Dunbar                                         LLVM_CONFIG=self.install_prefix + "/bin/llvm-config")))
4219ef81066SDaniel Dunbar
4229ef81066SDaniel Dunbar        make_install_flags = dict(
4239ef81066SDaniel Dunbar            llvm=dict(debug=["install"],
4249ef81066SDaniel Dunbar                      release=["install"],
4259ef81066SDaniel Dunbar                      paranoid=["install"]),
4269ef81066SDaniel Dunbar            dragonegg=dict(debug=["install"],
4279ef81066SDaniel Dunbar                           release=["install"],
4289ef81066SDaniel Dunbar                           paranoid=["install"]))
4299ef81066SDaniel Dunbar
4309ef81066SDaniel Dunbar        make_install_env = dict(
4319ef81066SDaniel Dunbar            llvm=dict(debug=dict(),
4329ef81066SDaniel Dunbar                      release=dict(),
4339ef81066SDaniel Dunbar                      paranoid=dict()),
4349ef81066SDaniel Dunbar            dragonegg=dict(debug=dict(),
4359ef81066SDaniel Dunbar                           release=dict(),
4369ef81066SDaniel Dunbar                           paranoid=dict()))
4379ef81066SDaniel Dunbar
4389ef81066SDaniel Dunbar        make_check_flags = dict(
4399ef81066SDaniel Dunbar            llvm=dict(debug=["check"],
4409ef81066SDaniel Dunbar                      release=["check"],
4419ef81066SDaniel Dunbar                      paranoid=["check"]),
4429ef81066SDaniel Dunbar            dragonegg=dict(debug=["check"],
4439ef81066SDaniel Dunbar                           release=["check"],
4449ef81066SDaniel Dunbar                           paranoid=["check"]))
4459ef81066SDaniel Dunbar
4469ef81066SDaniel Dunbar        make_check_env = dict(
4479ef81066SDaniel Dunbar            llvm=dict(debug=dict(),
4489ef81066SDaniel Dunbar                      release=dict(),
4499ef81066SDaniel Dunbar                      paranoid=dict()),
4509ef81066SDaniel Dunbar            dragonegg=dict(debug=dict(),
4519ef81066SDaniel Dunbar                           release=dict(),
4529ef81066SDaniel Dunbar                           paranoid=dict()))
4539ef81066SDaniel Dunbar
4549ef81066SDaniel Dunbar        for component in components:
4559ef81066SDaniel Dunbar            comp = component[:]
4569ef81066SDaniel Dunbar
457c26e5fb8SDavid Greene            if (self.options.no_dragonegg):
458c26e5fb8SDavid Greene                if (comp == 'dragonegg'):
4599ef81066SDaniel Dunbar                    self.logger.info("Skipping " + component + " in "
4609ef81066SDaniel Dunbar                                     + builddir)
4619ef81066SDaniel Dunbar                    continue
4629ef81066SDaniel Dunbar
4639ef81066SDaniel Dunbar            srcdir = source + "/" + comp.rstrip("2")
4649ef81066SDaniel Dunbar            builddir = self.build_prefix + "/" + comp + "/" + build_suffix
4659ef81066SDaniel Dunbar            installdir = self.install_prefix
4669ef81066SDaniel Dunbar
4679ef81066SDaniel Dunbar            comp_key = comp.replace("-", "_")
4689ef81066SDaniel Dunbar
4699ef81066SDaniel Dunbar            config_args = configure_flags[comp_key][build][:]
4709ef81066SDaniel Dunbar            config_args.extend(getattr(self.options,
4719ef81066SDaniel Dunbar                                       "extra_" + comp_key.rstrip("2")
472c26e5fb8SDavid Greene                                       + "_config_flags",
473c26e5fb8SDavid Greene                                       "").split())
4749ef81066SDaniel Dunbar
4759ef81066SDaniel Dunbar            self.logger.info("Configuring " + component + " in " + builddir)
476a15b16f2SDavid Greene            configrc = self.configure(component, srcdir, builddir,
4779ef81066SDaniel Dunbar                                      config_args,
4789ef81066SDaniel Dunbar                                      configure_env[comp_key][build])
4799ef81066SDaniel Dunbar
480a15b16f2SDavid Greene            if (configrc == None) :
481a15b16f2SDavid Greene                self.logger.info("[None] Failed to configure " + component + " in " + installdir)
482a15b16f2SDavid Greene
483a15b16f2SDavid Greene            if (configrc == 0 or self.options.keep_going) :
4849ef81066SDaniel Dunbar                self.logger.info("Building " + component + " in " + builddir)
4859ef81066SDaniel Dunbar                self.logger.info("Build: make " + str(make_flags[comp_key][build]))
486a15b16f2SDavid Greene                buildrc = self.make(component, srcdir, builddir,
4879ef81066SDaniel Dunbar                                    make_flags[comp_key][build],
4889ef81066SDaniel Dunbar                                    make_env[comp_key][build])
4899ef81066SDaniel Dunbar
490a15b16f2SDavid Greene                if (buildrc == None) :
491a15b16f2SDavid Greene                    self.logger.info("[None] Failed to build " + component + " in " + installdir)
492a15b16f2SDavid Greene
493a15b16f2SDavid Greene                if (buildrc == 0 or self.options.keep_going) :
494a15b16f2SDavid Greene                    self.logger.info("Testing " + component + " in " + builddir)
495a15b16f2SDavid Greene                    self.logger.info("Test: make "
496a15b16f2SDavid Greene                                     + str(make_check_flags[comp_key][build]))
497a15b16f2SDavid Greene                    testrc = self.make(component, srcdir, builddir,
498a15b16f2SDavid Greene                                       make_check_flags[comp_key][build],
499a15b16f2SDavid Greene                                       make_check_env[comp_key][build])
500a15b16f2SDavid Greene
501a15b16f2SDavid Greene                    if (testrc == None) :
502a15b16f2SDavid Greene                        self.logger.info("[None] Failed to test " + component + " in " + installdir)
503a15b16f2SDavid Greene
504a15b16f2SDavid Greene                    if ((testrc == 0  or self.options.keep_going)
505a15b16f2SDavid Greene                        and not self.options.no_install):
5069ef81066SDaniel Dunbar                        self.logger.info("Installing " + component + " in " + installdir)
5079ef81066SDaniel Dunbar                        self.make(component, srcdir, builddir,
5089ef81066SDaniel Dunbar                                  make_install_flags[comp_key][build],
5099ef81066SDaniel Dunbar                                  make_install_env[comp_key][build])
510a15b16f2SDavid Greene                    else :
511a15b16f2SDavid Greene                        self.logger.info("Failed testing " + component + " in " + installdir)
5129ef81066SDaniel Dunbar
513a15b16f2SDavid Greene                else :
514a15b16f2SDavid Greene                    self.logger.info("Failed to build " + component + " in " + installdir)
5159ef81066SDaniel Dunbar
516a15b16f2SDavid Greene            else :
517a15b16f2SDavid Greene                self.logger.info("Failed to configure " + component + " in " + installdir)
5189ef81066SDaniel Dunbar
5199ef81066SDaniel Dunbar    def configure(self, component, srcdir, builddir, flags, env):
520a15b16f2SDavid Greene        prefix = self.component_abbrev[component.replace("-", "_")]
521a15b16f2SDavid Greene
5229ef81066SDaniel Dunbar        self.logger.debug("Configure " + str(flags) + " " + str(srcdir) + " -> "
5239ef81066SDaniel Dunbar                          + str(builddir))
5249ef81066SDaniel Dunbar
5259ef81066SDaniel Dunbar        configure_files = dict(
5269ef81066SDaniel Dunbar            llvm=[(srcdir + "/configure", builddir + "/Makefile")],
527a15b16f2SDavid Greene            dragonegg=[(None,None)])
5289ef81066SDaniel Dunbar
5299ef81066SDaniel Dunbar
5309ef81066SDaniel Dunbar        doconfig = False
5319ef81066SDaniel Dunbar        for conf, mf in configure_files[component.replace("-", "_")]:
532a15b16f2SDavid Greene            if conf is None:
533a15b16f2SDavid Greene                # No configure necessary
534a15b16f2SDavid Greene                return 0
535a15b16f2SDavid Greene
5369ef81066SDaniel Dunbar            if not os.path.exists(conf):
537a15b16f2SDavid Greene                self.logger.info("[" + prefix + "] Configure failed, no configure script " + conf)
538a15b16f2SDavid Greene                return -1
539a15b16f2SDavid Greene
5409ef81066SDaniel Dunbar            if os.path.exists(conf) and os.path.exists(mf):
5419ef81066SDaniel Dunbar                confstat = os.stat(conf)
5429ef81066SDaniel Dunbar                makestat = os.stat(mf)
5439ef81066SDaniel Dunbar                if confstat.st_mtime > makestat.st_mtime:
5449ef81066SDaniel Dunbar                    doconfig = True
5459ef81066SDaniel Dunbar                    break
5469ef81066SDaniel Dunbar            else:
5479ef81066SDaniel Dunbar                doconfig = True
5489ef81066SDaniel Dunbar                break
5499ef81066SDaniel Dunbar
5509ef81066SDaniel Dunbar        if not doconfig and not self.options.force_configure:
551a15b16f2SDavid Greene            return 0
5529ef81066SDaniel Dunbar
5539ef81066SDaniel Dunbar        program = srcdir + "/configure"
5549ef81066SDaniel Dunbar        if not is_executable(program):
555a15b16f2SDavid Greene            self.logger.info("[" + prefix + "] Configure failed, cannot execute " + program)
556a15b16f2SDavid Greene            return -1
5579ef81066SDaniel Dunbar
5589ef81066SDaniel Dunbar        args = [program]
5599ef81066SDaniel Dunbar        args += ["--verbose"]
5609ef81066SDaniel Dunbar        args += flags
561a15b16f2SDavid Greene        return self.execute(args, builddir, env, component)
5629ef81066SDaniel Dunbar
5639ef81066SDaniel Dunbar    def make(self, component, srcdir, builddir, flags, env):
5649ef81066SDaniel Dunbar        program = find_executable("make")
5659ef81066SDaniel Dunbar        if program is None:
5669ef81066SDaniel Dunbar            raise ExecutableNotFound
5679ef81066SDaniel Dunbar
5689ef81066SDaniel Dunbar        if not is_executable(program):
5699ef81066SDaniel Dunbar            raise FileNotExecutable
5709ef81066SDaniel Dunbar
5719ef81066SDaniel Dunbar        args = [program]
5729ef81066SDaniel Dunbar        args += flags
573a15b16f2SDavid Greene        return self.execute(args, builddir, env, component)
5749ef81066SDaniel Dunbar
5759ef81066SDaniel Dunbar# Global constants
5769ef81066SDaniel Dunbarbuild_abbrev = dict(debug="dbg", release="opt", paranoid="par")
577c26e5fb8SDavid Greenecomponents = ["llvm", "dragonegg"]
5789ef81066SDaniel Dunbar
5799ef81066SDaniel Dunbar# Parse options
5809ef81066SDaniel Dunbarparser = optparse.OptionParser(version="%prog 1.0")
5819ef81066SDaniel Dunbaradd_options(parser)
5829ef81066SDaniel Dunbar(options, args) = parser.parse_args()
5839ef81066SDaniel Dunbarcheck_options(parser, options, build_abbrev.keys());
5849ef81066SDaniel Dunbar
5859ef81066SDaniel Dunbarif options.verbose:
5869ef81066SDaniel Dunbar    logging.basicConfig(level=logging.DEBUG,
5879ef81066SDaniel Dunbar                        format='%(name)-13s: %(message)s')
5889ef81066SDaniel Dunbarelse:
5899ef81066SDaniel Dunbar    logging.basicConfig(level=logging.INFO,
5909ef81066SDaniel Dunbar                        format='%(name)-13s: %(message)s')
5919ef81066SDaniel Dunbar
5929ef81066SDaniel Dunbarsource_abbrev = get_path_abbrevs(set(options.src))
5939ef81066SDaniel Dunbar
5949ef81066SDaniel Dunbarwork_queue = queue.Queue()
5959ef81066SDaniel Dunbar
5969ef81066SDaniel Dunbarjobs = options.jobs // options.threads
5979ef81066SDaniel Dunbarif jobs == 0:
5989ef81066SDaniel Dunbar    jobs = 1
5999ef81066SDaniel Dunbar
6009ef81066SDaniel Dunbarnumthreads = options.threads
6019ef81066SDaniel Dunbar
6029ef81066SDaniel Dunbarlogging.getLogger().info("Building with " + str(options.jobs) + " jobs and "
6039ef81066SDaniel Dunbar                         + str(numthreads) + " threads using " + str(jobs)
6049ef81066SDaniel Dunbar                         + " make jobs")
6059ef81066SDaniel Dunbar
606c26e5fb8SDavid Greenelogging.getLogger().info("CC  = " + str(options.cc))
607c26e5fb8SDavid Greenelogging.getLogger().info("CXX = " + str(options.cxx))
608c26e5fb8SDavid Greene
6099ef81066SDaniel Dunbarfor t in range(numthreads):
6109ef81066SDaniel Dunbar    builder = Builder(work_queue, jobs,
611c26e5fb8SDavid Greene                      build_abbrev, source_abbrev,
6129ef81066SDaniel Dunbar                      options)
6139ef81066SDaniel Dunbar    builder.daemon = True
6149ef81066SDaniel Dunbar    builder.start()
6159ef81066SDaniel Dunbar
6169ef81066SDaniel Dunbarfor build in set(options.build):
6179ef81066SDaniel Dunbar    for source in set(options.src):
618c26e5fb8SDavid Greene        work_queue.put((source, build))
6199ef81066SDaniel Dunbar
6209ef81066SDaniel Dunbarwork_queue.join()
621