924 lines
36 KiB
Python
924 lines
36 KiB
Python
# -*- python -*-
|
|
# ex: set syntax=python:
|
|
|
|
|
|
## TODO:
|
|
##
|
|
## - Add comments on every function/class
|
|
## - License stuff (on all files)
|
|
## - Cross testing (needed?)
|
|
## - Improve way to store and compare testcases
|
|
|
|
|
|
from buildbot.schedulers.basic import SingleBranchScheduler, AnyBranchScheduler
|
|
from buildbot.schedulers.forcesched import ForceScheduler
|
|
from buildbot.process import factory
|
|
from buildbot.process.properties import WithProperties
|
|
from buildbot.steps.shell import Compile
|
|
from buildbot.steps.shell import Configure
|
|
from buildbot.steps.shell import ShellCommand
|
|
from buildbot.steps.shell import SetPropertyFromCommand
|
|
from buildbot.steps.transfer import FileUpload
|
|
from buildbot.steps.source.git import Git
|
|
from buildbot.steps.slave import RemoveDirectory
|
|
from buildbot.changes.filter import ChangeFilter
|
|
from buildbot.buildslave import BuildSlave
|
|
from buildbot.status.results import SUCCESS, WARNINGS, FAILURE, EXCEPTION
|
|
from gdbcommand import CopyOldGDBSumFile, GdbCatSumfileCommand
|
|
from gdbgitdb import SaveGDBResults, get_builder_commit_id
|
|
from urllib import quote
|
|
|
|
from sumfiles import DejaResults, set_web_base
|
|
import os.path
|
|
import urllib
|
|
from json import load
|
|
import re
|
|
|
|
####################################
|
|
####################################
|
|
#### GDB BuildBot Configuration ####
|
|
####################################
|
|
####################################
|
|
|
|
###############################
|
|
#### General Configuration ####
|
|
###############################
|
|
|
|
# This is the dictionary that the buildmaster pays attention to. We
|
|
# also use a shorter alias to save typing.
|
|
c = BuildmasterConfig = {}
|
|
|
|
# Base directory for the web server. This is needed in order to
|
|
# compare the test results.
|
|
gdb_web_base = os.path.expanduser (os.path.join (basedir,
|
|
'public_html',
|
|
'results'))
|
|
set_web_base (gdb_web_base)
|
|
|
|
GDB_MAIL_FROM = 'sergiodj+buildbot@redhat.com'
|
|
GDB_MAIL_TO = 'gdb-testers@sourceware.org'
|
|
|
|
# 'protocols' contains information about protocols which master will use for
|
|
# communicating with slaves.
|
|
c['protocols'] = {'pb': {'port': 16123}}
|
|
|
|
# the 'change_source' setting tells the buildmaster how it should find out
|
|
# about source code changes.
|
|
|
|
# RE representing which branches to track on the GDB repository
|
|
branches_to_watch = re.compile ("(refs/heads/)?(master|gdb-\d+\.\d+-branch)")
|
|
|
|
# Function which decides whether BRANCH should be used or not
|
|
def should_watch_branch (branch):
|
|
if re.match (branches_to_watch, branch):
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
from buildbot.changes.gitpoller import GitPoller
|
|
c['change_source'] = []
|
|
c['change_source'].append(GitPoller(
|
|
repourl = 'git://git.libreplanetbr.org/gdb.git',
|
|
workdir = os.path.expanduser (os.path.join ('~/', 'buildbot-master-binutils-gdb')),
|
|
branches = should_watch_branch,
|
|
pollinterval = 60 * 3))
|
|
|
|
# 'status' is a list of Status Targets. The results of each build will be
|
|
# pushed to these targets. buildbot/status/*.py has a variety to choose from,
|
|
# including web pages, email senders, and IRC bots.
|
|
|
|
c['status'] = []
|
|
|
|
# Catch things like PR gdb/42, PR16, PR 16 or bug #11,
|
|
# and turn them into gdb bugzilla URLs.
|
|
cc_re_tuple = (r'(PR [a-z]+/|PR ?|#)(\d+)',
|
|
r'http://sourceware.org/bugzilla/show_bug.cgi?id=\2')
|
|
|
|
from buildbot.status import html
|
|
from buildbot.status.web import authz, auth
|
|
|
|
## The following class is a hack. It is needed because Builbot's
|
|
## webserver treats everything it doesn't know as text/html. Sigh...
|
|
# class WebStatusWithTextDefault(html.WebStatus):
|
|
# def __init__ (self, http_port, authz, **kwargs):
|
|
# html.WebStatus.__init__ (self, http_port = http_port,
|
|
# authz = authz, **kwargs)
|
|
|
|
# def setupSite(self):
|
|
# result = html.WebStatus.setupSite(self)
|
|
# self.site.resource.defaultType = r"text/plain"
|
|
# return result
|
|
|
|
authz_cfg=authz.Authz(
|
|
# change any of these to True to enable; see the manual for more
|
|
# options
|
|
# auth=auth.BasicAuth([("t","t")]),
|
|
gracefulShutdown = False,
|
|
forceBuild = True, # use this to test your slave once it is set up
|
|
forceAllBuilds = True, # ..or this
|
|
pingBuilder = False,
|
|
stopBuild = True,
|
|
stopAllBuilds = True,
|
|
cancelPendingBuild = True,
|
|
)
|
|
#c['status'].append(WebStatusWithTextDefault (http_port=8010, authz=authz_cfg))
|
|
c['status'].append (html.WebStatus (http_port = 8010, authz = authz_cfg))
|
|
|
|
#c['status'].append(html.WebStatus(http_port=8010,
|
|
# forceBuild = True,
|
|
# allowForce=False,
|
|
# order_console_by_time=True,
|
|
# changecommentlink=cc_re_tuple))
|
|
|
|
#from buildbot.status import words
|
|
#c['status'].append(words.IRC(host="irc.yyz.redhat.com", nick="sdj-gdbbot",
|
|
# channels=["#gdbbuild"]))
|
|
|
|
import smtplib
|
|
import socket
|
|
from email.mime.text import MIMEText
|
|
|
|
def SendRootMessageGDBTesters (branch, change):
|
|
global GDB_MAIL_TO, GDB_MAIL_FROM
|
|
|
|
rev = change.revision
|
|
f = "/tmp/gdb-buildbot-%s.lock" % rev
|
|
|
|
if os.path.exists (f):
|
|
# The message has already been sent
|
|
return
|
|
|
|
# WE HAVE TO REMEMBER TO CLEAN THESE FILES REGULARLY
|
|
open (f, 'w').close ()
|
|
|
|
text = ""
|
|
text += "*** TEST RESULTS FOR COMMIT %s ***\n\n" % rev
|
|
|
|
text += "Author: %s\n" % change.who
|
|
text += "Branch: %s\n" % branch
|
|
text += "Commit: %s\n\n" % rev
|
|
|
|
text += change.comments.split ('\n')[0] + "\n\n"
|
|
text += '\n'.join (change.comments.split ('\n')[1:])
|
|
|
|
chg_title = change.comments.split ('\n')[0]
|
|
mail = MIMEText (text)
|
|
if branch == 'master':
|
|
sbj = "[binutils-gdb] %s" % chg_title
|
|
else:
|
|
sbj = "[binutils-gdb/%s] %s" % (branch, chg_title)
|
|
|
|
mail['Subject'] = sbj
|
|
mail['From'] = GDB_MAIL_FROM
|
|
mail['To'] = GDB_MAIL_TO
|
|
mail['Message-Id'] = "<%s@gdb-build>" % rev
|
|
|
|
s = smtplib.SMTP ('localhost')
|
|
s.sendmail (GDB_MAIL_FROM, [ GDB_MAIL_TO ], mail.as_string ())
|
|
s.quit ()
|
|
|
|
def make_breakage_lockfile_name (builder):
|
|
return "/tmp/gdb-buildbot-breakage-report-%s.lock" % builder
|
|
|
|
def SendAuthorMessage (name, change, text_prepend):
|
|
"""Send a message to the author of the commit if it broke GDB.
|
|
|
|
We use a lock file to avoid reporting the breakage to different
|
|
people. This may happen, for example, if a commit X breaks GDB, but
|
|
subsequent commits are made after X, by different people."""
|
|
global GDB_MAIL_FROM
|
|
|
|
lockfile = make_breakage_lockfile_name (name)
|
|
|
|
if os.path.exists (lockfile):
|
|
# This means we have already reported this failure for this
|
|
# builder to the author.
|
|
return
|
|
|
|
# This file will be cleaned the next time we run
|
|
# MessageGDBTesters, iff the build breakage has been fixed.
|
|
open (lockfile, 'w').close ()
|
|
|
|
rev = change.revision
|
|
to = change.who
|
|
title = change.comments.split ('\n')[0]
|
|
|
|
sbj = 'Your commit \'%s\' broke GDB' % title
|
|
|
|
text = "Hello there,\n\n"
|
|
text += "Your commit:\n\n"
|
|
text += "\t%s\n" % title
|
|
text += "\t%s\n\n" % rev
|
|
text += "broke GDB. Please fix it, or the GDB gods will get you.\n\n"
|
|
text += "You can find details of the breakage below.\n\n"
|
|
text += "Cheers,\n\n"
|
|
text += "Your GDB BuildBot.\n\n"
|
|
text += "+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n\n"
|
|
text += "\n" + text_prepend
|
|
|
|
mail = MIMEText (text)
|
|
mail['Subject'] = sbj
|
|
mail['From'] = GDB_MAIL_FROM
|
|
mail['To'] = to
|
|
|
|
s = smtplib.SMTP ('localhost')
|
|
s.sendmail (GDB_MAIL_FROM, [ to ], mail.as_string ())
|
|
s.quit ()
|
|
|
|
def MessageGDBTesters (mode, name, build, results, master_status):
|
|
"""This function is responsible for composing the message that will be
|
|
send to the gdb-testers mailing list."""
|
|
git_url = "http://gdb-build.sergiodj.net/cgit"
|
|
branch = build.getSourceStamps ()[0].branch
|
|
cur_change = build.getSourceStamps ()[0].changes[0]
|
|
properties = build.getProperties ()
|
|
isrebuild = properties.getProperty ('isRebuild')
|
|
|
|
# Sending the root message to gdb-testers.
|
|
SendRootMessageGDBTesters (branch, cur_change)
|
|
|
|
# Subject
|
|
subj = "Failures on %s, branch %s" % (name, branch)
|
|
|
|
# Body
|
|
text = ""
|
|
|
|
# Buildslave name, useful for knowing the exact configuration.
|
|
text += "Buildslave:\n"
|
|
text += "\t%s\n" % build.getSlavename ()
|
|
|
|
# Including the link for the full build
|
|
text += "\nFull Build URL:\n"
|
|
text += "\t<%s>\n" % master_status.getURLForThing (build)
|
|
|
|
# Commits that were tested. Usually we should be dealing with
|
|
# only one commit
|
|
text += "\nCommit(s) tested:\n"
|
|
ss_list = build.getSourceStamps ()
|
|
for ss in ss_list:
|
|
for chg in ss.changes:
|
|
text += "\t%s\n" % chg.revision
|
|
|
|
# Who's to blame?
|
|
text += "\nAuthor(s) (in the same order as the commits):\n"
|
|
for ss in ss_list:
|
|
for chg in ss.changes:
|
|
text += "\t%s\n" % chg.who
|
|
|
|
# Subject of the changes
|
|
text += "\nSubject:\n"
|
|
text += "\t%s\n" % cur_change.comments.split ('\n')[0]
|
|
|
|
# URL to find more info about what went wrong.
|
|
text += "\nTestsuite log (gdb.sum and gdb.log) URL(s):\n"
|
|
for ss in ss_list:
|
|
commit_id = get_builder_commit_id (name, ss.revision, ss.branch)
|
|
if commit_id:
|
|
text += "\t<%s/%s/.git/tree/?h=%s&id=%s>\n" % (git_url, name, quote (ss.branch),
|
|
commit_id)
|
|
else:
|
|
text += "\t<Error fetching commit ID for %s>\n" % ss.revision
|
|
|
|
# Including the 'regressions' log. This is the 'diff' of what
|
|
# went wrong.
|
|
text += "\n"
|
|
if isrebuild and isrebuild == 'yes':
|
|
text += "\n*** WARNING: This was a REBUILD request! ***\n"
|
|
text += "*** The previous build (build #%s) MAY NOT BE the ancestor of the current build! ***\n\n" % properties.getProperty ('buildnumber')
|
|
|
|
# report_build_breakage will be True if we see a build breakage,
|
|
# i.e., if the 'configure' or the 'compile' steps fail. In this
|
|
# case, we use this variable to know if we must report the
|
|
# breakage directly to the author.
|
|
report_build_breakage = False
|
|
|
|
# found_regressions will be True if the 'regressions' log is not
|
|
# empty.
|
|
found_regressions = False
|
|
|
|
for log in build.getLogs ():
|
|
st = log.getStep ()
|
|
if st.getResults ()[0] == FAILURE:
|
|
n = st.getName ()
|
|
if 'No space left on device' in log.getText ():
|
|
text += "*** Internal error on buildslave (no space left on device). ***\n"
|
|
text += "*** Please report this to the buildslave owner (see <%s/buildslaves/%s>) ***\n\n" % (master_status.getBuildbotURL (), build.getSlavename ())
|
|
continue
|
|
elif n == 'update gdb master repo':
|
|
text += "*** Failed to update master GDB git repository. The build can continue. ***\n\n"
|
|
continue
|
|
elif n == 'update gdb repo':
|
|
text += "*** Failed to update GDB git repository. This is probably a timeout problem. ***\n\n"
|
|
break
|
|
elif n == 'configure gdb':
|
|
text += "*** Failed to configure GDB. ***\n"
|
|
text += "============================\n"
|
|
text += log.getText ()
|
|
text += "============================\n"
|
|
report_build_breakage = True
|
|
break
|
|
elif n == 'compile gdb':
|
|
text += "*** Failed to compiled GDB. ***\n"
|
|
text += "============================\n"
|
|
ct = log.getText ().decode ('ascii', 'ignore')
|
|
if len (ct) > 100000:
|
|
text += "\n+++ The full log is too big to be posted here."
|
|
text += "\n+++ These are the last 100 lines of it.\n\n"
|
|
ctt = ct.split ('\n')[-100:]
|
|
ct = '\n'.join (ctt)
|
|
text += ct
|
|
else:
|
|
text += ct
|
|
text += "============================\n"
|
|
report_build_breakage = True
|
|
break
|
|
elif n == 'make tags':
|
|
# We do not want to break here, because if this step
|
|
# fails the test will continue.
|
|
text += "*** Failed to make TAGS ***\n"
|
|
text += "Log URL: <%s/steps/%s/logs/%s>\n\n" % (master_status.getURLForThing (build),
|
|
quote (n), quote (log.getName ()))
|
|
continue
|
|
elif n == 'regressions' and log.getName () == 'regressions':
|
|
text += "*** Regressions found ***\n"
|
|
text += "============================\n"
|
|
text += log.getText ()
|
|
text += "============================\n"
|
|
found_regressions = True
|
|
break
|
|
|
|
# Including the 'xfail' log. It is important to say which tests
|
|
# we are ignoring.
|
|
if found_regressions:
|
|
for log in build.getLogs ():
|
|
if log.getStep ().getName () == 'regressions' and log.getName () == 'baseline_diff':
|
|
text += "\n\n*** Regressions against the baseline ***\n"
|
|
text += "============================\n"
|
|
text += log.getText ()
|
|
text += "============================\n"
|
|
break
|
|
|
|
xfail = os.path.join (gdb_web_base, name, 'xfails', branch, 'xfail')
|
|
if os.path.exists (xfail):
|
|
text += "\n\n*** Failures that are being ignored ***\n"
|
|
text += "============================\n"
|
|
with open (xfail, 'r') as f:
|
|
text += f.read ()
|
|
text += "============================\n"
|
|
text += "\n"
|
|
|
|
if report_build_breakage:
|
|
SendAuthorMessage (name, cur_change, text)
|
|
else:
|
|
# There is no build breakage anymore! Yay! Now, let's see if
|
|
# we need to clean up any lock file from previous breaks.
|
|
lockfile = make_breakage_lockfile_name (name)
|
|
if os.path.exists (lockfile):
|
|
# We need to clean the lockfile. Garbage-collect it here.
|
|
os.remove (lockfile)
|
|
|
|
return { 'body' : text,
|
|
'type' : 'plain',
|
|
'subject' : subj }
|
|
|
|
from buildbot.status import mail
|
|
mn = mail.MailNotifier(fromaddr = GDB_MAIL_FROM,
|
|
sendToInterestedUsers = False,
|
|
extraRecipients = [ GDB_MAIL_TO ],
|
|
mode = ('failing'),
|
|
messageFormatter = MessageGDBTesters,
|
|
extraHeaders = { 'X-GDB-Buildbot' : '1',
|
|
'In-Reply-To' : WithProperties ("<%s@gdb-build>",
|
|
'got_revision') })
|
|
|
|
c['status'].append (mn)
|
|
|
|
c['title'] = "GDB"
|
|
c['titleURL'] = "https://gnu.org/s/gdb"
|
|
|
|
c['buildbotURL'] = "http://gdb-build.sergiodj.net/"
|
|
|
|
c['db'] = {
|
|
'db_url' : "sqlite:///state.sqlite",
|
|
}
|
|
|
|
#####################
|
|
#### Build steps ####
|
|
#####################
|
|
|
|
## This is where we define our build steps. A build step is some
|
|
## command/action that buildbot will perform while building GDB. See
|
|
## the documentation on each build step class to understand what it
|
|
## does.
|
|
|
|
class CloneOrUpdateGDBMasterRepo (Git):
|
|
"""This build step updates the so-called "master" git repository. For
|
|
each buildslave, we have one master GDB git repository, which is then
|
|
used to create each builder's GDB git repository. In other words,
|
|
each builder inside a buildslave will have something like:
|
|
|
|
-- master GDB git repo
|
|
|
|
|
|---- builder X
|
|
|
|
|
|-- GDB git repo (clone from the master above)
|
|
|
|
|
|----- builder Y
|
|
|
|
|
|-- GDB git repo (clone from the master above)
|
|
|
|
and so on. This layout helps us to save some when fetching changes
|
|
from the principal repository."""
|
|
name = "update gdb master repo"
|
|
description = r"fetching GDB master sources"
|
|
descriptionDone = r"fetched GDB master sources"
|
|
def __init__ (self):
|
|
Git.__init__ (self,
|
|
repourl = 'git://git.libreplanetbr.org/gdb.git',
|
|
workdir = WithProperties (r"%s/../binutils-gdb-master/",
|
|
r'builddir'),
|
|
retryFetch = True,
|
|
mode = r'incremental',
|
|
progress = True)
|
|
self.haltOnFailure = False
|
|
self.flunkOnFailure = False
|
|
|
|
class CloneOrUpdateGDBRepo (Git):
|
|
"""This build step is used to clone the GDB git repository that will
|
|
be used by an specific builder (inside a buildslave). The trick here
|
|
is to use the "reference" parameter to initialize the class, which
|
|
makes BuildBot clone the git repository mostly using the objects
|
|
present at the reference repository (i.e., locally)."""
|
|
name = "clone gdb repo"
|
|
description = "fetching GDB sources"
|
|
descriptionDone = "fetched GDB sources"
|
|
def __init__ (self):
|
|
Git.__init__ (self,
|
|
repourl = 'git://git.libreplanetbr.org/gdb.git',
|
|
workdir = WithProperties ('%s/binutils-gdb/', 'builddir'),
|
|
reference = WithProperties ("%s/../binutils-gdb-master/",
|
|
'builddir'),
|
|
retryFetch = True,
|
|
progress = True)
|
|
|
|
class ConfigureGDB (Configure):
|
|
"""This build step runs the GDB "configure" command, providing extra
|
|
flags for it if needed."""
|
|
name = "configure gdb"
|
|
description = r"configure GDB"
|
|
descriptionDone = r"configured GDB"
|
|
def __init__ (self, extra_conf_flags, **kwargs):
|
|
Configure.__init__ (self, **kwargs)
|
|
self.workdir = WithProperties (r"%s", r'builddir')
|
|
self.command = ['../binutils-gdb/configure',
|
|
'--disable-binutils',
|
|
'--disable-ld',
|
|
'--disable-gold',
|
|
'--disable-gas',
|
|
'--disable-sim',
|
|
'--disable-gprof'] + extra_conf_flags
|
|
|
|
class CompileGDB (Compile):
|
|
"""This build step runs "make" to compile the GDB sources. It
|
|
provides extra "make" flags to "make" if needed. It also uses the
|
|
"jobs" properties to figure out how many parallel jobs we can use when
|
|
compiling GDB; this is the "-j" flag for "make". The value of the
|
|
"jobs" property is set at the "config.json" file, for each
|
|
buildslave."""
|
|
name = "compile gdb"
|
|
description = r"compile GDB"
|
|
descriptionDone = r"compiled GDB"
|
|
def __init__ (self, make_command = 'make', extra_make_flags = [],
|
|
**kwargs):
|
|
Compile.__init__ (self, **kwargs)
|
|
self.workdir = WithProperties (r"%s", r'builddir')
|
|
self.command = ['%s' % make_command,
|
|
WithProperties (r"-j%s", r'jobs'),
|
|
'all'] + extra_make_flags
|
|
|
|
class MakeTAGSGDB (ShellCommand):
|
|
name = 'make tags'
|
|
description = 'running make TAGS'
|
|
descriptionDone = 'ran make TAGS'
|
|
def __init__ (self, **kwargs):
|
|
ShellCommand.__init__ (self, make_command = 'make',
|
|
**kwargs)
|
|
self.workdir = WithProperties ("%s/build/gdb", 'builddir')
|
|
self.command = [ '%s' % make_command, 'TAGS' ]
|
|
# We do not want to stop testing when this command fails.
|
|
self.haltOnFailure = False
|
|
self.flunkOnFailure = False
|
|
self.flunkOnWarnings = False
|
|
|
|
class TestGDB (ShellCommand):
|
|
"""This build step runs the full testsuite for GDB. It can run in
|
|
parallel mode (see BuildAndTestGDBFactory below), and it will also
|
|
provide any extra flags for "make" if needed. Unfortunately, because
|
|
our testsuite is not perfect (yet), this command must not make
|
|
BuildBot halt on failure."""
|
|
name = "test gdb"
|
|
description = r"testing GDB"
|
|
descriptionDone = r"tested GDB"
|
|
def __init__ (self, make_command = 'make', extra_make_check_flags = [],
|
|
test_env = {}, **kwargs):
|
|
ShellCommand.__init__ (self, decodeRC = { 0 : SUCCESS,
|
|
1 : SUCCESS,
|
|
2 : SUCCESS },
|
|
**kwargs)
|
|
|
|
self.workdir = WithProperties (r"%s/build/gdb/testsuite", r'builddir')
|
|
self.command = ['%s' % make_command,
|
|
'-k',
|
|
'check'] + extra_make_check_flags
|
|
|
|
self.env = test_env
|
|
# Needed because of dejagnu
|
|
self.haltOnFailure = False
|
|
self.flunkOnFailure = False
|
|
self.flunkOnWarnings = False
|
|
|
|
|
|
#######################
|
|
#### Build Factory ####
|
|
#######################
|
|
|
|
## This is where our Build Factory is defined. A build factory is a
|
|
## description of the build process, which is made in terms of build
|
|
## steps. The BuildAndTestGDBFactory is the main build factory for
|
|
## GDB; it is configurable and should be more than enough to describe
|
|
## most builds.
|
|
|
|
class BuildAndTestGDBFactory (factory.BuildFactory):
|
|
"""This is the main build factory for the GDB project. It was made to
|
|
be very configurable, and should be enough to describe most builds.
|
|
The parameters of the class are:
|
|
|
|
- ConfigureClass: set this to be the class (i.e., build step) that
|
|
will be called to configure GDB. It needs to accept the same
|
|
arguments as the ConfigureGDB class above. The default is to
|
|
use ConfigureGDB.
|
|
|
|
- CompileClass: set this to be the class (i.e., build step) that
|
|
will be called to compile GDB. It needs to accept the same
|
|
arguments as the CompileGDB class above. The default is to use
|
|
CompileGDB.
|
|
|
|
- TestClass: set this to be the class (i.e., build step) that will
|
|
be called to test GDB. It needs to accept the same arguments as
|
|
the TestGDB class above. The default is to use TestGDB.
|
|
|
|
- extra_conf_flags: extra flags to be passed to "configure".
|
|
Should be a list (i.e., []). The default is None.
|
|
|
|
- enable_targets_all: set this to True to pass
|
|
'--enable-targets=all' to configure. The default is True.
|
|
|
|
- extra_make_flags: extra flags to be passed to "make", when
|
|
compiling. Should be a list (i.e., []). The default is None.
|
|
|
|
- extra_make_check_flags: extra flags to be passed to "make
|
|
check", when testing. Should be a list (i.e., []). The default
|
|
is None.
|
|
|
|
- test_env: extra environment variables to be passed to "make
|
|
check", when testing. Should be a dictionary (i.e., {}). The
|
|
default is None.
|
|
|
|
- test_parallel: set to True if the test shall be parallelized.
|
|
Default is False. Beware that parallelizing tests may cause
|
|
some failures due to limited system resources.
|
|
|
|
- make_command: set the command that will be called when running
|
|
'make'. This is needed because BSD systems need to run 'gmake'
|
|
instead of make. Default is 'make'.
|
|
|
|
- use_system_debuginfo: set to False if GDB should be compiled
|
|
with the "--with-separate-debug-dir" option set to
|
|
"/usr/lib/debug". Default is True.
|
|
|
|
"""
|
|
ConfigureClass = ConfigureGDB
|
|
CompileClass = CompileGDB
|
|
TestClass = TestGDB
|
|
|
|
# Set this to False to skip the test
|
|
run_testsuite = True
|
|
|
|
extra_conf_flags = None
|
|
enable_targets_all = True
|
|
|
|
extra_make_flags = None
|
|
extra_make_check_flags = None
|
|
test_env = None
|
|
|
|
# Set this to false to disable parallel testing (i.e., do not use
|
|
# FORCE_PARALLEL)
|
|
test_parallel = True
|
|
|
|
# Set this to the make command that you want to run in the "make"
|
|
# steps.
|
|
make_command = 'make'
|
|
|
|
# Set this to False to disable using system's debuginfo files
|
|
# (i.e., do not use '--with-separate-debug-dir')
|
|
use_system_debuginfo = True
|
|
|
|
def __init__ (self, architecture_triplet = [], initial_delay = None):
|
|
factory.BuildFactory.__init__ (self)
|
|
|
|
self.architecture_triplet = architecture_triplet
|
|
|
|
# mjw asked me to delay the build by X number of seconds.
|
|
if initial_delay:
|
|
self.addStep (ShellCommand (command = ['sleep', '%d' % initial_delay],
|
|
description = "delaying start of build by %d seconds" % initial_delay,
|
|
descriptionDone = "delayed start of build by %d seconds" % initial_delay))
|
|
|
|
self.addStep (RemoveDirectory (dir = WithProperties (r"%s/build",
|
|
r'builddir'),
|
|
description = r"removing old build dir",
|
|
descriptionDone = r"removed old build dir"))
|
|
self.addStep (CloneOrUpdateGDBMasterRepo ())
|
|
self.addStep (CloneOrUpdateGDBRepo ())
|
|
|
|
if self.run_testsuite:
|
|
self.addStep (CopyOldGDBSumFile ())
|
|
|
|
if not self.extra_conf_flags:
|
|
self.extra_conf_flags = []
|
|
|
|
if self.enable_targets_all:
|
|
self.extra_conf_flags.append (r'--enable-targets=all')
|
|
|
|
if self.use_system_debuginfo:
|
|
self.extra_conf_flags.append (r'--with-separate-debug-dir=/usr/lib/debug')
|
|
|
|
self.addStep (self.ConfigureClass (self.extra_conf_flags + architecture_triplet))
|
|
|
|
if not self.extra_make_flags:
|
|
self.extra_make_flags = []
|
|
self.addStep (self.CompileClass (self.make_command, self.extra_make_flags))
|
|
|
|
# Disabling this until we figure out how to properly run + test
|
|
# self.addStep (MakeTAGSGDB ())
|
|
|
|
if not self.extra_make_check_flags:
|
|
self.extra_make_check_flags = []
|
|
|
|
if self.run_testsuite:
|
|
if not self.test_env:
|
|
self.test_env = {}
|
|
|
|
if self.test_parallel:
|
|
self.extra_make_check_flags.append (WithProperties (r"-j%s", r'jobs'))
|
|
self.extra_make_check_flags.append (r'FORCE_PARALLEL=1')
|
|
|
|
self.addStep (self.TestClass (self.make_command, self.extra_make_check_flags,
|
|
self.test_env))
|
|
|
|
self.addStep (GdbCatSumfileCommand (workdir = WithProperties (r'%s/build/gdb/testsuite',
|
|
r'builddir'),
|
|
description = r'analyze test results'))
|
|
self.addStep (FileUpload (slavesrc = WithProperties (r"%s/build/gdb/testsuite/gdb.log",
|
|
r'builddir'),
|
|
masterdest = WithProperties (r"public_html/results/%s/gdb.log",
|
|
r'buildername')))
|
|
self.addStep (SaveGDBResults ())
|
|
|
|
|
|
##################
|
|
#### Builders ####
|
|
##################
|
|
|
|
## This section describes our builders. The builders are instances of
|
|
## a build factory, and they will be used to do a specific build of
|
|
## the project.
|
|
##
|
|
## The nomenclature here is important. Every builder should start
|
|
## with the prefix "RunTestGDB", and then be followed by the testing
|
|
## scenario that the build will test, followed by "_cXXtYY", where XX
|
|
## is the bitness of the compilation, and YY is the bitness of the
|
|
## testing. So, for example, if we are specifically testing GDB
|
|
## running the native-gdbserver tests, compiling GDB on a 64-bit
|
|
## machine, but running the tests in 32-bit mode, our builder would be called:
|
|
##
|
|
## RunTestGDBNativeGDBServer_c64t32
|
|
|
|
class RunTestGDBPlain_c64t64 (BuildAndTestGDBFactory):
|
|
"""Compiling for 64-bit, testing on 64-bit."""
|
|
def __init__ (self, **kwargs):
|
|
BuildAndTestGDBFactory.__init__ (self, **kwargs)
|
|
|
|
class RunTestGDBPlain_c32t32 (BuildAndTestGDBFactory):
|
|
"""Compiling on 32-bit, testing on 32-bit."""
|
|
def __init__ (self, **kwargs):
|
|
self.extra_conf_flags = [ r'CFLAGS=-m32' ]
|
|
self.extra_make_check_flags = [ r'RUNTESTFLAGS=--target_board unix/-m32' ]
|
|
BuildAndTestGDBFactory.__init__ (self, **kwargs)
|
|
|
|
class RunTestGDBm32_c64t32 (BuildAndTestGDBFactory):
|
|
"""Compiling on 64-bit, testing on 32-bit."""
|
|
def __init__ (self, **kwargs):
|
|
self.extra_make_check_flags = [ r'RUNTESTFLAGS=--target_board unix/-m32' ]
|
|
BuildAndTestGDBFactory.__init__ (self, **kwargs)
|
|
|
|
class RunTestGDBNativeGDBServer_c64t64 (BuildAndTestGDBFactory):
|
|
"""Compiling on 64-bit, testing native-gdbserver on 64-bit."""
|
|
def __init__ (self, **kwargs):
|
|
self.extra_make_check_flags = [ r'RUNTESTFLAGS=--target_board native-gdbserver' ]
|
|
BuildAndTestGDBFactory.__init__ (self, **kwargs)
|
|
|
|
class RunTestGDBNativeGDBServer_c64t32 (BuildAndTestGDBFactory):
|
|
"""Compiling on 64-bit, testing native-gdbserver on 32-bit."""
|
|
def __init__ (self, **kwargs):
|
|
self.extra_make_check_flags = [ r'RUNTESTFLAGS=--target_board native-gdbserver/-m32' ]
|
|
BuildAndTestGDBFactory.__init__ (self, **kwargs)
|
|
|
|
class RunTestGDBNativeGDBServer_c32t32 (BuildAndTestGDBFactory):
|
|
"""Compiling on 32-bit, testing native-gdbserver on 32-bit."""
|
|
def __init__ (self, **kwargs):
|
|
self.extra_conf_flags = [ 'CFLAGS=-m32']
|
|
self.extra_make_check_flags = [ 'RUNTESTFLAGS=--target_board native-gdbserver/-m32']
|
|
BuildAndTestGDBFactory.__init__ (self, **kwargs)
|
|
|
|
class RunTestGDBNativeExtendedGDBServer_c64t64 (BuildAndTestGDBFactory):
|
|
"""Compiling on 64-bit, testing native-extended-gdbserver on 64-bit."""
|
|
def __init__ (self, **kwargs):
|
|
self.extra_make_check_flags = [ r'RUNTESTFLAGS=--target_board native-extended-gdbserver' ]
|
|
BuildAndTestGDBFactory.__init__ (self, **kwargs)
|
|
|
|
class RunTestGDBNativeExtendedGDBServer_c64t32 (BuildAndTestGDBFactory):
|
|
"""Compiling on 64-bit, testing native-extended-gdbserver on 32-bit."""
|
|
def __init__ (self, **kwargs):
|
|
self.extra_make_check_flags = [ r'RUNTESTFLAGS=--target_board native-extended-gdbserver/-m32' ]
|
|
BuildAndTestGDBFactory.__init__ (self, **kwargs)
|
|
|
|
class RunTestGDBNativeExtendedGDBServer_c32t32 (BuildAndTestGDBFactory):
|
|
"""Compiling on 64-bit, testing native-extended-gdbserver on 32-bit."""
|
|
def __init__ (self, **kwargs):
|
|
self.extra_conf_flags = [ 'CFLAGS=-m32']
|
|
self.extra_make_check_flags = [ r'RUNTESTFLAGS=--target_board native-extended-gdbserver/-m32' ]
|
|
BuildAndTestGDBFactory.__init__ (self, **kwargs)
|
|
|
|
class RunTestGDBIndexBuild (BuildAndTestGDBFactory):
|
|
"""Testing with the "cc-with-tweaks.sh" passing -i."""
|
|
def __init__ (self, **kwargs):
|
|
self.extra_make_check_flags = [ WithProperties (r'CC_FOR_TARGET=/bin/sh %s/binutils-gdb/gdb/contrib/cc-with-tweaks.sh -i gcc', r'builddir'),
|
|
WithProperties (r'CXX_FOR_TARGET=/bin/sh %s/binutils-gdb/gdb/contrib/cc-with-tweaks.sh -i g++', r'builddir') ]
|
|
BuildAndTestGDBFactory.__init__ (self, **kwargs)
|
|
|
|
class RunTestGDBIndexBuild_c32t32 (BuildAndTestGDBFactory):
|
|
"""Testing with the "cc-with-tweaks.sh" passing -i. 32-bit version"""
|
|
def __init__ (self, **kwargs):
|
|
self.extra_conf_flags = [ 'CFLAGS=-m32' ]
|
|
self.extra_make_check_flags = [ WithProperties (r'CC_FOR_TARGET=/bin/sh %s/binutils-gdb/gdb/contrib/cc-with-tweaks.sh -i gcc', r'builddir'),
|
|
WithProperties (r'CXX_FOR_TARGET=/bin/sh %s/binutils-gdb/gdb/contrib/cc-with-tweaks.sh -i g++', r'builddir'),
|
|
'RUNTESTFLAGS=--target_board unix/-m32' ]
|
|
BuildAndTestGDBFactory.__init__ (self, **kwargs)
|
|
|
|
# Class for only building GDB, without testing
|
|
|
|
class RunTestGDBPlainBuildWithCxx_c64notest (BuildAndTestGDBFactory):
|
|
"""Compiling for 64-bit with --enable-build-with-cxx. We do not test
|
|
anything for now."""
|
|
def __init__ (self, **kwargs):
|
|
self.extra_conf_flags = [ '--enable-build-with-cxx' ]
|
|
self.run_testsuite = False
|
|
BuildAndTestGDBFactory.__init__ (self, **kwargs)
|
|
|
|
# Classes needed for BSD systems
|
|
|
|
class RunTestGDBBSD_Common (BuildAndTestGDBFactory):
|
|
"""Common BSD test configurations"""
|
|
def __init__ (self, **kwargs):
|
|
self.extra_conf_flags = [ 'CFLAGS=-I/usr/local/include', '--disable-werror' ]
|
|
self.make_command = 'gmake'
|
|
BuildAndTestGDBFactory.__init__ (self, **kwargs)
|
|
|
|
class RunTestGDBPlainBSD_c64t64 (RunTestGDBPlain_c64t64, RunTestGDBBSD_Common):
|
|
"""Compiling for 64-bit, testing on 64-bit."""
|
|
pass
|
|
|
|
class RunTestGDBIndexBuildBSD (RunTestGDBIndexBuild, RunTestGDBBSD_Common):
|
|
"""Testing with the "cc-with-tweaks.sh" passing -i. FIXME: include bitness here."""
|
|
pass
|
|
|
|
# Classes needed for AIX systems
|
|
|
|
class RunTestGDBAIX_Common (BuildAndTestGDBFactory):
|
|
"""Common AIX test configurations"""
|
|
def __init__ (self, **kwargs):
|
|
# Unfortunately we have to disable -Werror there...
|
|
self.extra_conf_flags = [ '--disable-werror' ]
|
|
self.enable_targets_all = False
|
|
self.make_command = 'gmake'
|
|
self.extra_make_flags = [ 'MAKEINFO=true' ]
|
|
BuildAndTestGDBFactory.__init__ (self, **kwargs)
|
|
|
|
class RunTestGDBPlainAIX (RunTestGDBAIX_Common, RunTestGDBPlain_c64t64):
|
|
"""Compiling for AIX"""
|
|
pass
|
|
|
|
# All branches that are going to be watched.
|
|
all_gdb_filter = ChangeFilter (branch_fn = should_watch_branch)
|
|
|
|
# This function prevents a builder to build more than one build at the
|
|
# same time. This is needed because we do not have a way to lock the
|
|
# git repository containing the test results of the builder, so
|
|
# simultaneous builds can cause a mess when committing the test
|
|
# results.
|
|
def DefaultGDBCanStartBuild (builder, buildslave, buildrequest):
|
|
return not builder.building
|
|
|
|
files_ignored_re = re.compile ("(binutils/|cpu/|elfcpp/|gas/|gold/|gprof/|ld/|texinfo/|gdb/doc/).*")
|
|
|
|
def DefaultGDBfileIsImportant (change):
|
|
"""Implementation of fileIsImportant method, in order to decide which
|
|
changes to build on GDB."""
|
|
only_changelog = True
|
|
|
|
# Do not build the 'GDB Administrator' commits, that are used to
|
|
# increment the date on some files.
|
|
if 'GDB Administrator' in change.who:
|
|
return False
|
|
|
|
# Filter out commits that only modify the ChangeLog files.
|
|
for filename in change.files:
|
|
if 'ChangeLog' not in filename:
|
|
only_changelog = False
|
|
break
|
|
|
|
if only_changelog:
|
|
return False
|
|
|
|
for filename in change.files:
|
|
if not re.match (files_ignored_re, filename):
|
|
return True
|
|
|
|
return False
|
|
|
|
###############################
|
|
#### Configuration loading ####
|
|
###############################
|
|
|
|
## This is "the heart" of this file. This function is responsible for
|
|
## loading the configuration present in the "lib/config.json" file,
|
|
## and initializing everything needed for BuildBot to work. Most of
|
|
## this function was copied from WebKit's BuildBot configuration, with
|
|
## lots of tweaks.
|
|
|
|
def load_config (c):
|
|
config = load (open ("lib/config.json"))
|
|
passwd = load (open ("lib/passwords.json"))
|
|
|
|
c['slaves'] = [BuildSlave (slave['name'], passwd[slave['name']],
|
|
max_builds = 1,
|
|
notify_on_missing = [ str (slave['admin']) ],
|
|
missing_timeout = 300,
|
|
properties = { 'jobs' : slave['jobs'] })
|
|
for slave in config['slaves']]
|
|
|
|
c['schedulers'] = []
|
|
for s in config['schedulers']:
|
|
if "change_filter" in s:
|
|
s['change_filter'] = globals ()[s['change_filter']]
|
|
s['treeStableTimer'] = None
|
|
s['fileIsImportant'] = DefaultGDBfileIsImportant
|
|
kls = globals ()[s.pop ('type')]
|
|
s = dict (map (lambda key_value_pair : (str (key_value_pair[0]),
|
|
key_value_pair[1]),
|
|
s.items ()))
|
|
c['schedulers'].append (kls (**s))
|
|
|
|
c['builders'] = []
|
|
for b in config['builders']:
|
|
myenv = {}
|
|
|
|
if 'arch_triplet' in b:
|
|
architecture_triplet = [ b.pop ('arch_triplet') ]
|
|
else:
|
|
architecture_triplet = []
|
|
|
|
if 'initial_delay' in b:
|
|
initial_delay = int (b.pop ('initial_delay'))
|
|
else:
|
|
initial_delay = None
|
|
|
|
btype = b.pop ('type')
|
|
factory = globals ()[ "RunTestGDB%s" % btype ]
|
|
b['factory'] = factory (architecture_triplet = architecture_triplet,
|
|
initial_delay = initial_delay)
|
|
b['canStartBuild'] = DefaultGDBCanStartBuild
|
|
b['mergeRequests'] = False
|
|
|
|
# AIX hack. Sigh...
|
|
try:
|
|
mypath = b.pop ('PATH')
|
|
myenv['PATH'] = mypath
|
|
b['env'] = myenv
|
|
except KeyError:
|
|
pass
|
|
|
|
c['builders'].append (b)
|
|
|
|
load_config (c)
|