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