]> git.ipfire.org Git - pakfire.git/blame - python/pakfire/builder.py
Cleanup BUILDROOT before starting a build.
[pakfire.git] / python / pakfire / builder.py
CommitLineData
47a4cb89 1#!/usr/bin/python
b792d887
MT
2###############################################################################
3# #
4# Pakfire - The IPFire package management system #
5# Copyright (C) 2011 Pakfire development team #
6# #
7# This program is free software: you can redistribute it and/or modify #
8# it under the terms of the GNU General Public License as published by #
9# the Free Software Foundation, either version 3 of the License, or #
10# (at your option) any later version. #
11# #
12# This program is distributed in the hope that it will be useful, #
13# but WITHOUT ANY WARRANTY; without even the implied warranty of #
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
15# GNU General Public License for more details. #
16# #
17# You should have received a copy of the GNU General Public License #
18# along with this program. If not, see <http://www.gnu.org/licenses/>. #
19# #
20###############################################################################
47a4cb89
MT
21
22import fcntl
23import grp
24import logging
d59bde4c 25import math
47a4cb89
MT
26import os
27import re
28import shutil
fc4d4177 29import socket
47a4cb89 30import time
f02283bb 31import uuid
47a4cb89 32
7c8f2953 33import base
93bd0aa4 34import chroot
9eac53ba 35import logger
47a4cb89 36import packages
e0636b31 37import packages.packager
fa6d335b 38import repository
47a4cb89
MT
39import util
40
41from constants import *
4496b160 42from i18n import _
e9c20259 43from errors import BuildError, BuildRootLocked, Error
47a4cb89
MT
44
45
9eac53ba
MT
46BUILD_LOG_HEADER = """
47 ____ _ __ _ _ _ _ _
48| _ \ __ _| | __/ _(_)_ __ ___ | |__ _ _(_) | __| | ___ _ __
49| |_) / _` | |/ / |_| | '__/ _ \ | '_ \| | | | | |/ _` |/ _ \ '__|
50| __/ (_| | <| _| | | | __/ | |_) | |_| | | | (_| | __/ |
51|_| \__,_|_|\_\_| |_|_| \___| |_.__/ \__,_|_|_|\__,_|\___|_|
52
53 Time : %(time)s
54 Host : %(host)s
55 Version : %(version)s
56
57"""
58
c07a3ca7 59class BuildEnviron(object):
47a4cb89
MT
60 # The version of the kernel this machine is running.
61 kernel_version = os.uname()[2]
62
9eac53ba 63 def __init__(self, pkg=None, distro_config=None, build_id=None, logfile=None,
f22069bb
MT
64 builder_mode="release", **pakfire_args):
65 # Set mode.
66 assert builder_mode in ("development", "release",)
67 self.mode = builder_mode
68
69 # Disable the build repository in release mode.
70 if self.mode == "release":
71 if pakfire_args.has_key("disable_repos") and pakfire_args["disable_repos"]:
72 pakfire_args["disable_repos"] += ["build",]
73 else:
74 pakfire_args["disable_repos"] = ["build",]
75
9eac53ba
MT
76 # Save the build id and generate one if no build id was provided.
77 if not build_id:
78 build_id = "%s" % uuid.uuid4()
79
80 self.build_id = build_id
81
82 # Setup the logging.
83 if logfile:
84 self.log = logging.getLogger(self.build_id)
85 # Propage everything to the root logger that we will see something
86 # on the terminal.
87 self.log.propagate = 1
88 self.log.setLevel(logging.INFO)
89
90 # Add the given logfile to the logger.
91 h = logging.FileHandler(logfile)
92 self.log.addHandler(h)
93
94 # Format the log output for the file.
95 f = logger.BuildFormatter()
96 h.setFormatter(f)
97 else:
98 # If no logile was given, we use the root logger.
99 self.log = logging.getLogger()
100
8330691a
MT
101 # Log information about pakfire and some more information, when we
102 # are running in release mode.
103 if self.mode == "release":
104 logdata = {
105 "host" : socket.gethostname(),
106 "time" : time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime()),
107 "version" : "Pakfire %s" % PAKFIRE_VERSION,
108 }
109
110 for line in BUILD_LOG_HEADER.splitlines():
111 self.log.info(line % logdata)
9eac53ba 112
7c8f2953 113 # Create pakfire instance.
1ee5d54a
MT
114 if pakfire_args.has_key("mode"):
115 del pakfire_args["mode"]
eb34496a 116 self.pakfire = base.Pakfire(mode="builder", distro_config=distro_config, **pakfire_args)
7c8f2953
MT
117 self.distro = self.pakfire.distro
118 self.path = self.pakfire.path
119
c07a3ca7
MT
120 # Log the package information.
121 self.pkg = packages.Makefile(self.pakfire, pkg)
122 self.log.info(_("Package information:"))
123 for line in self.pkg.dump(long=True).splitlines():
124 self.log.info(" %s" % line)
125 self.log.info("")
7c8f2953 126
062699ee
MT
127 # Download all package files.
128 self.pkg.download()
129
7c8f2953 130 # XXX need to make this configureable
47a4cb89
MT
131 self.settings = {
132 "enable_loop_devices" : True,
33f4679b 133 "enable_ccache" : True,
8c716255 134 "enable_icecream" : False,
47a4cb89 135 }
7c8f2953 136 #self.settings.update(settings)
47a4cb89 137
47a4cb89
MT
138 # Lock the buildroot
139 self._lock = None
140 self.lock()
141
fc4d4177
MT
142 # Save the build time.
143 self.build_time = int(time.time())
f02283bb 144
8930b228
MT
145 def start(self):
146 # Mount the directories.
147 self._mountall()
148
9a416dbc
MT
149 # Populate /dev.
150 self.populate_dev()
151
83e5f0da
MT
152 # Setup domain name resolution in chroot.
153 self.setup_dns()
8930b228
MT
154
155 # Extract all needed packages.
156 self.extract()
157
158 def stop(self):
159 # Kill all still running processes.
160 util.orphans_kill(self.path)
161
162 # Close pakfire instance.
163 del self.pakfire
164
165 # Umount the build environment.
166 self._umountall()
167
168 # Remove all files.
169 self.destroy()
170
7c8f2953
MT
171 @property
172 def arch(self):
173 """
174 Inherit architecture from distribution configuration.
175 """
176 return self.distro.arch
177
fc4d4177
MT
178 @property
179 def info(self):
180 return {
181 "build_date" : time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime(self.build_time)),
182 "build_host" : socket.gethostname(),
183 "build_id" : self.build_id,
184 "build_time" : self.build_time,
185 }
186
47a4cb89
MT
187 def lock(self):
188 filename = os.path.join(self.path, ".lock")
189
190 try:
191 self._lock = open(filename, "a+")
192 except IOError, e:
193 return 0
194
195 try:
196 fcntl.lockf(self._lock.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB)
197 except IOError, e:
198 raise BuildRootLocked, "Buildroot is locked"
199
200 return 1
201
202 def unlock(self):
203 if self._lock:
204 self._lock.close()
205 self._lock = None
206
207 def copyin(self, file_out, file_in):
208 if file_in.startswith("/"):
209 file_in = file_in[1:]
210
211 file_in = self.chrootPath(file_in)
212
213 #if not os.path.exists(file_out):
214 # return
215
216 dir_in = os.path.dirname(file_in)
217 if not os.path.exists(dir_in):
218 os.makedirs(dir_in)
219
220 logging.debug("%s --> %s" % (file_out, file_in))
221
222 shutil.copy2(file_out, file_in)
223
224 def copyout(self, file_in, file_out):
225 if file_in.startswith("/"):
226 file_in = file_in[1:]
227
228 file_in = self.chrootPath(file_in)
229
230 #if not os.path.exists(file_in):
231 # return
232
233 dir_out = os.path.dirname(file_out)
234 if not os.path.exists(dir_out):
235 os.makedirs(dir_out)
236
237 logging.debug("%s --> %s" % (file_in, file_out))
238
239 shutil.copy2(file_in, file_out)
240
241 def copy_result(self, resultdir):
242 dir_in = self.chrootPath("result")
243
244 for dir, subdirs, files in os.walk(dir_in):
245 basename = os.path.basename(dir)
246 dir = dir[len(self.chrootPath()):]
247 for file in files:
248 file_in = os.path.join(dir, file)
249
250 file_out = os.path.join(
251 resultdir,
252 basename,
253 file,
254 )
255
256 self.copyout(file_in, file_out)
257
9c2ad426 258 def extract(self, requires=None, build_deps=True):
47a4cb89
MT
259 """
260 Gets a dependency set and extracts all packages
261 to the environment.
262 """
9c2ad426
MT
263 if not requires:
264 requires = []
5be98997 265
9c2ad426
MT
266 # Add neccessary build dependencies.
267 requires += BUILD_PACKAGES
3d960a21 268
33f4679b
MT
269 # If we have ccache enabled, we need to extract it
270 # to the build chroot.
271 if self.settings.get("enable_ccache"):
9c2ad426 272 requires.append("ccache")
33f4679b 273
5be98997
MT
274 # If we have icecream enabled, we need to extract it
275 # to the build chroot.
276 if self.settings.get("enable_icecream"):
9c2ad426 277 requires.append("icecream")
47a4cb89
MT
278
279 # Get build dependencies from source package.
c07a3ca7
MT
280 for req in self.pkg.requires:
281 requires.append(req)
47a4cb89 282
9c2ad426
MT
283 # Install all packages.
284 self.install(requires)
47a4cb89
MT
285
286 # Copy the makefile and load source tarballs.
c07a3ca7
MT
287 self.pkg.extract(_("Extracting"),
288 prefix=os.path.join(self.path, "build"))
47a4cb89 289
9c2ad426
MT
290 def install(self, requires):
291 """
292 Install everything that is required in requires.
293 """
c9ec78ca
MT
294 # If we got nothing to do, we quit immediately.
295 if not requires:
296 return
297
c07a3ca7
MT
298 self.pakfire.install(requires, interactive=False,
299 allow_downgrade=True, logger=self.log)
47a4cb89 300
c9ec78ca
MT
301 def install_test(self):
302 pkgs = []
c9ec78ca
MT
303 for dir, subdirs, files in os.walk(self.chrootPath("result")):
304 for file in files:
b88090e9 305 pkgs.append(os.path.join(dir, file))
c9ec78ca 306
325e6065 307 self.pakfire.localinstall(pkgs, yes=True, allow_uninstall=True)
c9ec78ca 308
47a4cb89
MT
309 def chrootPath(self, *args):
310 # Remove all leading slashes
311 _args = []
312 for arg in args:
313 if arg.startswith("/"):
314 arg = arg[1:]
315 _args.append(arg)
316 args = _args
317
318 ret = os.path.join(self.path, *args)
319 ret = ret.replace("//", "/")
320
321 assert ret.startswith(self.path)
322
323 return ret
324
9a416dbc 325 def populate_dev(self):
47a4cb89 326 nodes = [
9a416dbc
MT
327 "/dev/null",
328 "/dev/zero",
329 "/dev/full",
330 "/dev/random",
331 "/dev/urandom",
332 "/dev/tty",
333 "/dev/ptmx",
334 "/dev/kmsg",
335 "/dev/rtc0",
336 "/dev/console",
47a4cb89
MT
337 ]
338
339 # If we need loop devices (which are optional) we create them here.
340 if self.settings["enable_loop_devices"]:
341 for i in range(0, 7):
9a416dbc 342 nodes.append("/dev/loop%d" % i)
47a4cb89 343
47a4cb89 344 for node in nodes:
9a416dbc
MT
345 # Stat the original node of the host system and copy it to
346 # the build chroot.
347 node_stat = os.stat(node)
348
349 self._create_node(node, node_stat.st_mode, node_stat.st_rdev)
47a4cb89
MT
350
351 os.symlink("/proc/self/fd/0", self.chrootPath("dev", "stdin"))
352 os.symlink("/proc/self/fd/1", self.chrootPath("dev", "stdout"))
353 os.symlink("/proc/self/fd/2", self.chrootPath("dev", "stderr"))
354 os.symlink("/proc/self/fd", self.chrootPath("dev", "fd"))
355
83e5f0da 356 def setup_dns(self):
18973967
MT
357 """
358 Add DNS resolution facility to chroot environment by copying
359 /etc/resolv.conf and /etc/hosts.
360 """
361 for i in ("/etc/resolv.conf", "/etc/hosts"):
362 self.copyin(i, i)
47a4cb89
MT
363
364 def _create_node(self, filename, mode, device):
365 logging.debug("Create node: %s (%s)" % (filename, mode))
366
367 filename = self.chrootPath(filename)
368
369 # Create parent directory if it is missing.
370 dirname = os.path.dirname(filename)
371 if not os.path.exists(dirname):
372 os.makedirs(dirname)
373
374 os.mknod(filename, mode, device)
375
526c3e7f 376 def destroy(self):
e412b8dc 377 logging.debug("Destroying environment %s" % self.path)
47a4cb89
MT
378
379 if os.path.exists(self.path):
380 util.rm(self.path)
381
e412b8dc
MT
382 def cleanup(self):
383 logging.debug("Cleaning environemnt.")
384
e412b8dc 385 # Remove the build directory and buildroot.
0d96815e 386 dirs = ("build", "result")
e412b8dc
MT
387
388 for d in dirs:
389 d = self.chrootPath(d)
390 if not os.path.exists(d):
391 continue
392
393 util.rm(d)
394 os.makedirs(d)
395
47a4cb89
MT
396 def _mountall(self):
397 self.log.debug("Mounting environment")
8930b228
MT
398 for src, dest, fs, options in self.mountpoints:
399 mountpoint = self.chrootPath(dest)
400 if options:
401 options = "-o %s" % options
402
403 # Eventually create mountpoint directory
404 if not os.path.exists(mountpoint):
405 os.makedirs(mountpoint)
406
407 cmd = "mount -n -t %s %s %s %s" % \
408 (fs, options, src, mountpoint)
93bd0aa4 409 chroot.do(cmd, shell=True)
47a4cb89
MT
410
411 def _umountall(self):
412 self.log.debug("Umounting environment")
8930b228
MT
413
414 mountpoints = []
415 for src, dest, fs, options in reversed(self.mountpoints):
416 if not dest in mountpoints:
417 mountpoints.append(dest)
418
419 for dest in mountpoints:
420 mountpoint = self.chrootPath(dest)
421
422 chroot.do("umount -n %s" % mountpoint, raiseExc=0, shell=True)
47a4cb89
MT
423
424 @property
425 def mountpoints(self):
8930b228
MT
426 mountpoints = []
427
428 # Make root as a tmpfs.
429 #mountpoints += [
430 # ("pakfire_root", "/", "tmpfs", "defaults"),
431 #]
432
433 mountpoints += [
434 # src, dest, fs, options
435 ("pakfire_proc", "/proc", "proc", "nosuid,noexec,nodev"),
436 ("/proc/sys", "/proc/sys", "bind", "bind"),
437 ("/proc/sys", "/proc/sys", "bind", "bind,ro,remount"),
438 ("/sys", "/sys", "bind", "bind"),
439 ("/sys", "/sys", "bind", "bind,ro,remount"),
440 ("pakfire_tmpfs", "/dev", "tmpfs", "mode=755,nosuid"),
441 ("/dev/pts", "/dev/pts", "bind", "bind"),
442 ("pakfire_tmpfs", "/run", "tmpfs", "mode=755,nosuid,nodev"),
47a4cb89
MT
443 ]
444
8930b228
MT
445 # If selinux is enabled.
446 if os.path.exists("/sys/fs/selinux"):
447 mountpoints += [
448 ("/sys/fs/selinux", "/sys/fs/selinux", "bind", "bind"),
449 ("/sys/fs/selinux", "/sys/fs/selinux", "bind", "bind,ro,remount"),
450 ]
47a4cb89 451
8930b228 452 # If ccache support is requested, we bind mount the cache.
33f4679b 453 if self.settings.get("enable_ccache"):
046278b2
MT
454 # Create ccache cache directory if it does not exist.
455 if not os.path.exists(CCACHE_CACHE_DIR):
456 os.makedirs(CCACHE_CACHE_DIR)
457
8930b228
MT
458 mountpoints += [
459 (CCACHE_CACHE_DIR, "/var/cache/ccache", "bind", "bind"),
460 ]
33f4679b 461
8930b228 462 return mountpoints
47a4cb89
MT
463
464 @property
465 def environ(self):
466 env = {
0cf10c2d
MT
467 # Add HOME manually, because it is occasionally not set
468 # and some builds get in trouble then.
469 "HOME" : "/root",
89ebac67
MT
470 "TERM" : os.environ.get("TERM", "dumb"),
471 "PS1" : "\u:\w\$ ",
0cf10c2d 472
936f6b37
MT
473 # Set the container that we can detect, if we are inside a
474 # chroot.
475 "container" : "pakfire-builder",
47a4cb89
MT
476 }
477
478 # Inherit environment from distro
479 env.update(self.pakfire.distro.environ)
480
5be98997 481 # Icecream environment settings
c07a3ca7 482 if self.settings.get("enable_icecream", False):
5be98997
MT
483 # Set the toolchain path
484 if self.settings.get("icecream_toolchain", None):
485 env["ICECC_VERSION"] = self.settings.get("icecream_toolchain")
486
487 # Set preferred host if configured.
488 if self.settings.get("icecream_preferred_host", None):
489 env["ICECC_PREFERRED_HOST"] = \
490 self.settings.get("icecream_preferred_host")
491
47a4cb89
MT
492 # XXX what do we need else?
493
494 return env
495
9eac53ba 496 def do(self, command, shell=True, personality=None, logger=None, *args, **kwargs):
47a4cb89 497 ret = None
47a4cb89 498
8930b228
MT
499 # Environment variables
500 env = self.environ
47a4cb89 501
8930b228
MT
502 if kwargs.has_key("env"):
503 env.update(kwargs.pop("env"))
15398910 504
8930b228
MT
505 logging.debug("Environment:")
506 for k, v in sorted(env.items()):
507 logging.debug(" %s=%s" % (k, v))
e360ea59 508
8930b228
MT
509 # Update personality it none was set
510 if not personality:
511 personality = self.distro.personality
5be98997 512
8930b228
MT
513 # Make every shell to a login shell because we set a lot of
514 # environment things there.
515 if shell:
516 command = ["bash", "--login", "-c", command]
47a4cb89 517
8930b228
MT
518 if not kwargs.has_key("chrootPath"):
519 kwargs["chrootPath"] = self.chrootPath()
47a4cb89 520
8930b228
MT
521 ret = chroot.do(
522 command,
523 personality=personality,
524 shell=False,
525 env=env,
526 logger=logger,
527 *args,
528 **kwargs
529 )
47a4cb89
MT
530
531 return ret
532
75bb74a7 533 def build(self, install_test=True):
c157d1e2
MT
534 assert self.pkg
535
c07a3ca7
MT
536 pkgfile = os.path.join("/build", os.path.basename(self.pkg.filename))
537 resultdir = self.chrootPath("/result")
538
539 # Create the build command, that is executed in the chroot.
9b875540 540 build_command = ["/usr/lib/pakfire/builder", "--offline", "build", pkgfile,
75bb74a7 541 "--nodeps", "--resultdir=/result",]
c07a3ca7
MT
542
543 try:
544 self.do(" ".join(build_command), logger=self.log)
545
546 except Error:
547 raise BuildError, _("The build command failed. See logfile for details.")
548
75bb74a7
MT
549 # Perform install test.
550 if install_test:
551 self.install_test()
552
c07a3ca7
MT
553 # Copy the final packages and stuff.
554 # XXX TODO resultdir
47a4cb89
MT
555
556 def shell(self, args=[]):
e9c20259
MT
557 if not util.cli_is_interactive():
558 logging.warning("Cannot run shell on non-interactive console.")
559 return
560
9c2ad426
MT
561 # Install all packages that are needed to run a shell.
562 self.install(SHELL_PACKAGES)
563
47a4cb89 564 # XXX need to set CFLAGS here
83e5f0da
MT
565 command = "/usr/sbin/chroot %s %s %s" % \
566 (self.chrootPath(), SHELL_SCRIPT, " ".join(args))
47a4cb89 567
e360ea59
MT
568 # Add personality if we require one
569 if self.pakfire.distro.personality:
a7596ccf
MT
570 command = "%s %s" % (self.pakfire.distro.personality, command)
571
572 for key, val in self.environ.items():
573 command = "%s=\"%s\" " % (key, val) + command
e360ea59 574
47a4cb89 575 # Empty the environment
a7596ccf 576 command = "env -i - %s" % command
47a4cb89
MT
577
578 logging.debug("Shell command: %s" % command)
579
8930b228
MT
580 shell = os.system(command)
581 return os.WEXITSTATUS(shell)
c07a3ca7 582
56f5e5ff 583
ff9299d0 584class Builder(object):
c07a3ca7
MT
585 def __init__(self, pakfire, filename, resultdir, **kwargs):
586 self.pakfire = pakfire
587
588 self.filename = filename
589
590 self.resultdir = resultdir
591
592 # Open package file.
593 self.pkg = packages.Makefile(self.pakfire, self.filename)
594
c07a3ca7 595 self._environ = {
0d96815e 596 "LANG" : "C",
c07a3ca7
MT
597 }
598
0d96815e
MT
599 @property
600 def buildroot(self):
601 return self.pkg.buildroot
602
c07a3ca7
MT
603 @property
604 def distro(self):
605 return self.pakfire.distro
606
607 @property
608 def environ(self):
609 environ = os.environ
3c45a6af
MT
610
611 # Get all definitions from the package.
612 environ.update(self.pkg.exports)
613
614 # Overwrite some definitions by default values.
c07a3ca7
MT
615 environ.update(self._environ)
616
617 return environ
618
619 def do(self, command, shell=True, personality=None, cwd=None, *args, **kwargs):
620 # Environment variables
621 logging.debug("Environment:")
622 for k, v in sorted(self.environ.items()):
623 logging.debug(" %s=%s" % (k, v))
624
625 # Update personality it none was set
626 if not personality:
627 personality = self.distro.personality
628
629 if not cwd:
630 cwd = "/%s" % LOCAL_TMP_PATH
631
632 # Make every shell to a login shell because we set a lot of
633 # environment things there.
634 if shell:
635 command = ["bash", "--login", "-c", command]
636
637 return chroot.do(
638 command,
639 personality=personality,
640 shell=False,
641 env=self.environ,
642 logger=logging.getLogger(),
643 cwd=cwd,
644 *args,
645 **kwargs
646 )
647
648 def create_icecream_toolchain(self):
649 try:
9c6179fb 650 out = self.do("icecc --build-native 2>/dev/null", returnOutput=True)
c07a3ca7
MT
651 except Error:
652 return
653
654 for line in out.splitlines():
655 m = re.match(r"^creating ([a-z0-9]+\.tar\.gz)", line)
656 if m:
657 self._environ["icecream_toolchain"] = "/%s" % m.group(1)
658
659 def create_buildscript(self, stage):
660 file = "/tmp/build_%s" % util.random_string()
661
662 # Get buildscript from the package.
663 script = self.pkg.get_buildscript(stage)
664
665 # Write script to an empty file.
666 f = open(file, "w")
667 f.write("#!/bin/sh\n\n")
668 f.write("set -e\n")
669 f.write("set -x\n")
670 f.write("\n%s\n" % script)
671 f.write("exit 0\n")
672 f.close()
673 os.chmod(file, 700)
674
675 return file
676
677 def build(self):
7abe644a
MT
678 # Create buildroot and remove all content if it was existant.
679 util.rm(self.buildroot)
680 os.makedirs(self.buildroot)
c07a3ca7
MT
681
682 # Build icecream toolchain if icecream is installed.
683 self.create_icecream_toolchain()
684
685 for stage in ("prepare", "build", "test", "install"):
686 self.build_stage(stage)
687
688 # Package the result.
689 # Make all these little package from the build environment.
690 logging.info(_("Creating packages:"))
56f5e5ff 691 pkgs = []
c07a3ca7 692 for pkg in reversed(self.pkg.packages):
5dda54e4
MT
693 packager = packages.packager.BinaryPackager(self.pakfire, pkg,
694 self, self.buildroot)
56f5e5ff
MT
695 pkg = packager.run(self.resultdir)
696 pkgs.append(pkg)
697 logging.info("")
698
699 for pkg in sorted(pkgs):
700 for line in pkg.dump(long=True).splitlines():
701 logging.info(line)
702 logging.info("")
c07a3ca7
MT
703 logging.info("")
704
705 def build_stage(self, stage):
706 # Get the buildscript for this stage.
707 buildscript = self.create_buildscript(stage)
708
709 # Execute the buildscript of this stage.
710 logging.info(_("Running stage %s:") % stage)
c07a3ca7 711
75bb74a7
MT
712 try:
713 self.do(buildscript, shell=False)
714
715 finally:
716 # Remove the buildscript.
717 if os.path.exists(buildscript):
718 os.unlink(buildscript)
c07a3ca7
MT
719
720 def cleanup(self):
721 if os.path.exists(self.buildroot):
722 util.rm(self.buildroot)