extras-buildsys/builder builder.py,1.35,1.36
Daniel Williams (dcbw)
fedora-extras-commits at redhat.com
Wed Aug 31 01:56:40 UTC 2005
Author: dcbw
Update of /cvs/fedora/extras-buildsys/builder
In directory cvs-int.fedora.redhat.com:/tmp/cvs-serv22735/builder
Modified Files:
builder.py
Log Message:
2005-08-30 Dan Williams <dcbw at redhat.com>
* builder/builder.py
common/ExecUtils.py
- To execute mock, we now fork() and execv() the mock process
so that we can more reliably gather its output
- Correctly clean up the mock root directory
- Rename builder.log -> job.log so it can't get confused
with build.log from mock
Index: builder.py
===================================================================
RCS file: /cvs/fedora/extras-buildsys/builder/builder.py,v
retrieving revision 1.35
retrieving revision 1.36
diff -u -r1.35 -r1.36
--- builder.py 30 Aug 2005 21:47:20 -0000 1.35
+++ builder.py 31 Aug 2005 01:56:38 -0000 1.36
@@ -21,7 +21,6 @@
import socket
import os
import shutil
-import popen2
import sha
import time
import sys
@@ -37,6 +36,7 @@
from plague import AuthedXMLRPCServer
from plague import HTTPServer
from plague import daemonize
+from plague import ExecUtils
from optparse import OptionParser
sys.path.append('/usr/share/plague/builder')
@@ -88,13 +88,14 @@
self._repo_locked = True
self._repo_locked_msg = False
self._files = []
- self._pobj = None
+ self._childpid = 0
self._target_cfg = target_cfg
self._builder_cfg = target_cfg.parent_cfg()
self._srpm_url = srpm_url
self._log_fd = None
self._mock_config = None
self._done_status = ''
+ self._mock_log = None
self.buildroot = self._target_cfg.mock_config()
work_dir = self._builder_cfg.get_str("Directories", "builder_work_dir")
@@ -106,7 +107,7 @@
if not os.path.exists(self._state_dir):
os.makedirs(self._state_dir)
- logfile = os.path.join(self._result_dir, "builder.log")
+ logfile = os.path.join(self._result_dir, "job.log")
self._log_fd = open(logfile, "w+")
target_dict = self._target_cfg.target_dict()
@@ -137,17 +138,18 @@
def die(self, sig=15):
if self.is_done_status() or self._done_status == 'killed':
- return
+ return True
self._die = True
+ return True
def _handle_death(self):
self._log("Killing build process...\n")
# Don't try to kill a running cleanup process
- if self._status != 'cleanup' and self._pobj and self._pobj.pid:
+ if self._status != 'cleanup' and self._childpid:
try:
- os.kill(self._pobj.pid, 15)
+ os.kill(self._childpid, 15)
except OSError, e:
- self._log("Couldn't kill process %d: %s\n" % (self._pobj.pid, e))
+ self._log("Couldn't kill process %d: %s\n" % (self._childpid, e))
self._log("Killed.\n");
self._done_status = 'killed'
@@ -186,19 +188,47 @@
self._status = 'failed'
self._log("Failed to retrieve %s.\n" % url)
+ def _copy_mock_output_to_log(self):
+ if self._mock_log and os.path.exists(self._mock_log):
+ ml = open(self._mock_log, "r")
+ line = "foo"
+ while len(line):
+ line = ml.readline()
+ if len(line):
+ self._log_fd.write(line)
+ ml.close()
+ os.remove(self._mock_log)
+ self._mock_log = None
+
def _start_build(self):
self._log("Starting step 'building' with command:\n")
if not os.path.exists(self._result_dir):
os.makedirs(self._result_dir)
if not os.path.exists(self._result_dir):
os.makedirs(self._result_dir)
- mock_args = "-r %s --arch %s --resultdir=%s --statedir=%s --uniqueext=%s %s" % (self.buildroot,
- self.buildarch, self._result_dir, self._state_dir, self._uniqid, self._srpm_path)
- builder_cmd = self._builder_cfg.get_str("General", "builder_cmd")
- cmd = '%s %s %s' % (self.arch_command, builder_cmd, mock_args)
- self._log(" %s\n" % cmd)
- self._pobj = popen2.Popen4(cmd=cmd, bufsize=1024)
- fcntl.fcntl(self._pobj.fromchild.fileno(), fcntl.F_SETFL, os.O_NONBLOCK)
+
+ # Set up build process arguments
+ args = []
+ builder_cmd = os.path.abspath(self._builder_cfg.get_str("General", "builder_cmd"))
+ cmd = builder_cmd
+ if self.arch_command and len(self.arch_command):
+ arg_list = self.arch_command.split()
+ for arg in arg_list:
+ args.append(arg)
+ cmd = os.path.abspath(arg_list[0])
+ args.append(builder_cmd)
+ args.append("-r")
+ args.append(self.buildroot)
+ args.append("--arch")
+ args.append(self.buildarch)
+ args.append("--resultdir=%s" % self._result_dir)
+ args.append("--statedir=%s" % self._state_dir)
+ args.append("--uniqueext=%s" % self._uniqid)
+ args.append(self._srpm_path)
+ self._log(" %s\n" % string.join(args))
+
+ self._mock_log = os.path.join(self._result_dir, "mock-output.log")
+ self._childpid = ExecUtils.exec_with_redirect(cmd, args, None, self._mock_log, self._mock_log)
self._status = 'prepping'
# Poll a bit to wait for mock to write out the status file if
@@ -210,33 +240,54 @@
time.sleep(0.5)
except KeyboardInterrupt:
pass
+
# if mock exited with an error report that error and not
# the missing status file.
- exit_status = self._pobj.poll()
- if exit_status > 0:
+ (aux_pid, status) = os.waitpid(self._childpid, os.WNOHANG)
+ status = os.WEXITSTATUS(status)
+ if aux_pid:
+ # If mock exits anywhere here, something is wrong no matter
+ # what it's exit status
+ self._childpid = 0
+ self._copy_mock_output_to_log()
self._status = 'failed'
break
- # Kill mock after 7s if it didn't dump the status file
- if time.time() - start_time > 7:
+ # Kill mock after 15s if it didn't dump the status file
+ if time.time() - start_time > 15:
+ self._copy_mock_output_to_log()
self._log("Timed out waiting for the mock status file! %s\n" % mockstatusfile)
try:
self._log("Killing mock...\n")
- os.kill(self._pobj.pid, 15)
+ os.kill(self._childpid, 15)
except OSError, e:
- self._log("Couldn't kill mock process %d: %s\n" % (self._pobj.pid, e))
+ self._log("Couldn't kill mock process %d: %s\n" % (self._childpid, e))
else:
self._log("Killed.\n")
+
self._status = 'failed'
break
def _start_cleanup(self):
self._log("Cleaning up the buildroot...\n")
- builder_cmd = self._builder_cfg.get_str("General", "builder_cmd")
- cmd = '%s %s clean --uniqueext=%s -r %s' % (self.arch_command,
- builder_cmd, self._uniqid, self.buildroot)
- self._log(" %s\n" % cmd)
- self._pobj = popen2.Popen4(cmd=cmd)
+
+ args = []
+ builder_cmd = os.path.abspath(self._builder_cfg.get_str("General", "builder_cmd"))
+ cmd = builder_cmd
+ if self.arch_command and len(self.arch_command):
+ arg_list = self.arch_command.split()
+ for arg in arg_list:
+ args.append(arg)
+ cmd = os.path.abspath(arg_list[0])
+ args.append(builder_cmd)
+ args.append("clean")
+ args.append("--uniqueext=%s" % self._uniqid)
+ args.append("-r")
+ args.append(self.buildroot)
+
+ self._log(" %s\n" % string.join(args))
+ self._childpid = ExecUtils.exec_with_redirect(cmd, args, None, None, None)
+
self._status = 'cleanup'
def _mock_is_prepping(self):
@@ -309,37 +360,23 @@
f.close()
return contents
- def _grab_mock_output(self):
- """ Grab mock output and write it to a log """
- if self._pobj:
- string = ' '
- while len(string) > 0:
- try:
- string = os.read(self._pobj.fromchild.fileno(), 1024)
- except OSError, e:
- if e.errno == errno.EAGAIN: # Resource temporarily unavailable
- break
- else:
- self._log("Error reading mock output: %s\n" % e)
- else:
- # We don't care about output from the 'cleanup' stage
- if self._status != 'cleanup':
- self._log_fd.write(string)
- self._log_fd.flush()
- os.fsync(self._log_fd.fileno())
-
def _mock_done(self):
- # Ensure child mock is reaped
- if self._pobj:
- self._pobj.poll()
+ # Ensure child process is reaped
+ if self._childpid:
+ try:
+ (pid, status) = os.waitpid(self._childpid, 0)
+ except OSError, e:
+ self._childpid = 0
+ pass
+
+ self._copy_mock_output_to_log()
self._files = self._find_files()
self._log("\n\n-----------------------\n\n")
if self._status == 'done':
self._log("Job completed successfully.\n")
elif self._status == 'failed':
- if self._pobj:
- exit_status = self._pobj.poll()
+ if self._childpid:
self._log("Job failed due to mock errors! Please see output in root.log and build.log\n")
elif self._status == 'killed':
self._log("Job failed because it was killed.\n")
@@ -372,36 +409,31 @@
self._status = 'building'
def _status_building(self):
- exit_status = self._pobj.poll()
- if exit_status == 0:
- # mock completed successfully
- if self._status != 'building':
- self._log("Bad job end status %s encountered!" % self._status)
- self._done_status = 'done'
- self._start_cleanup()
- elif exit_status > 0:
- # mock exited with an error
- self._done_status = 'failed'
- self._start_cleanup()
+ (aux_pid, status) = os.waitpid(self._childpid, os.WNOHANG)
+ status = os.WEXITSTATUS(status)
+ if aux_pid:
+ self._childpid = 0
+ if status == 0:
+ self._done_status = 'done'
+ elif status > 0:
+ self._done_status = 'failed'
+
+ self._start_cleanup()
def _status_cleanup(self):
- exit_status = self._pobj.poll()
- if exit_status >= 0:
+ (aux_pid, status) = os.waitpid(self._childpid, os.WNOHANG)
+ if aux_pid:
# Mock exited
self._status = self._done_status
- if self._mock_config and self._mock_config.has_key('rootdir') and self._mock_config.has_key('statedir'):
- # Kill the entire job dir, not just the rootdir
- job_dir = os.path.normpath(self._mock_config['rootdir'] + "/../")
- job_dir2 = os.path.normpath(self._mock_config['statedir'] + "/../")
- print job_dir, job_dir2
-
- # Be a little paranoid about randomly removing an entire directory.
- # Compare the rootdir's parent to the statedir's parent and remove the
- # parent only if they match.
- if job_dir == job_dir2:
- shutil.rmtree(job_dir, ignore_errors=True)
- else:
- shutil.rmtree(self._mock_config['rootdir'], ignore_errors=True)
+ if self._mock_config:
+ if self._mock_config.has_key('rootdir'):
+ mock_root_dir = os.path.abspath(os.path.join(self._mock_config['rootdir'], "../"))
+ # Ensure we're actually deleteing the job's rootdir
+ if mock_root_dir.endswith(self._uniqid):
+ shutil.rmtree(mock_root_dir, ignore_errors=True)
+
+ if self._mock_config.has_key('statedir'):
+ shutil.rmtree(self._mock_config['statedir'], ignore_errors=True)
def run(self):
while True:
@@ -416,7 +448,6 @@
self._log("ERROR: internal builder inconsistency, didn't recognize status '%s'." % self._status)
self._status = 'failed'
- self._grab_mock_output()
if self.is_done_status():
self._mock_done()
break
More information about the fedora-extras-commits
mailing list