]>
git.ipfire.org Git - pakfire.git/blob - python/pakfire/builder.py
2 ###############################################################################
4 # Pakfire - The IPFire package management system #
5 # Copyright (C) 2011 Pakfire development team #
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. #
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. #
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/>. #
20 ###############################################################################
40 import packages
.packager
46 log
= logging
.getLogger("pakfire")
48 from config
import ConfigBuilder
49 from system
import system
50 from constants
import *
52 from errors
import BuildError
, BuildRootLocked
, Error
55 BUILD_LOG_HEADER
= """
57 | _ \ __ _| | __/ _(_)_ __ ___ | |__ _ _(_) | __| | ___ _ __
58 | |_) / _` | |/ / |_| | '__/ _ \ | '_ \| | | | | |/ _` |/ _ \ '__|
59 | __/ (_| | <| _| | | | __/ | |_) | |_| | | | (_| | __/ |
60 |_| \__,_|_|\_\_| |_|_| \___| |_.__/ \__,_|_|_|\__,_|\___|_|
63 Host : %(hostname)s (%(host_arch)s)
68 class BuildEnviron(object):
69 # The version of the kernel this machine is running.
70 kernel_version
= os
.uname()[2]
72 def __init__(self
, filename
=None, distro_name
=None, config
=None, configs
=None, arch
=None,
73 build_id
=None, logfile
=None, builder_mode
="release", use_cache
=None, **pakfire_args
):
75 assert builder_mode
in ("development", "release",)
76 self
.mode
= builder_mode
78 # Disable the build repository in release mode.
79 if self
.mode
== "release":
80 if pakfire_args
.has_key("disable_repos") and pakfire_args
["disable_repos"]:
81 pakfire_args
["disable_repos"] += ["build",]
83 pakfire_args
["disable_repos"] = ["build",]
85 # Save the build id and generate one if no build id was provided.
87 build_id
= "%s" % uuid
.uuid4()
89 self
.build_id
= build_id
93 self
.log
= log
.getChild(self
.build_id
)
94 # Propage everything to the root logger that we will see something
96 self
.log
.propagate
= 1
97 self
.log
.setLevel(logging
.INFO
)
99 # Add the given logfile to the logger.
100 h
= logging
.FileHandler(logfile
)
101 self
.log
.addHandler(h
)
103 # Format the log output for the file.
104 f
= logger
.BuildFormatter()
107 # If no logile was given, we use the root logger.
108 self
.log
= logging
.getLogger("pakfire")
110 # Initialize a cgroup (if supported).
112 if cgroup
.supported():
113 self
.cgroup
= cgroup
.CGroup("pakfire/builder/%s" % self
.build_id
)
115 # Attach the pakfire-builder process to the parent group.
116 self
.cgroup
.parent
.attach()
118 # Log information about pakfire and some more information, when we
119 # are running in release mode.
120 if self
.mode
== "release":
122 "host_arch" : system
.arch
,
123 "hostname" : system
.hostname
,
124 "time" : time
.strftime("%a, %d %b %Y %H:%M:%S +0000", time
.gmtime()),
125 "version" : "Pakfire %s" % PAKFIRE_VERSION
,
128 for line
in BUILD_LOG_HEADER
.splitlines():
129 self
.log
.info(line
% logdata
)
131 # Create pakfire instance.
132 if pakfire_args
.has_key("mode"):
133 del pakfire_args
["mode"]
136 config
= ConfigBuilder(files
=configs
)
139 if distro_name
is None:
140 distro_name
= config
.get("builder", "distro", None)
141 config
.load_distro_config(distro_name
)
143 if not config
.has_distro():
144 log
.error(_("You have not set the distribution for which you want to build."))
145 log
.error(_("Please do so in builder.conf or on the CLI."))
146 raise ConfigError
, _("Distribution configuration is missing.")
148 self
.pakfire
= base
.Pakfire(
155 self
.distro
= self
.pakfire
.distro
156 self
.path
= self
.pakfire
.path
158 # Check if this host can build the requested architecture.
159 if not system
.host_supports_arch(self
.arch
):
160 raise BuildError
, _("Cannot build for %s on this host.") % self
.arch
162 # Where do we put the result?
163 self
.resultdir
= os
.path
.join(self
.path
, "result")
165 # Check weather to use or not use the cache.
166 if use_cache
is None:
167 # If use_cache is None, the user did not provide anything and
169 if self
.mode
== "development":
174 self
.use_cache
= use_cache
177 # If we have a plain makefile, we first build a source package and go with that.
179 if filename
.endswith(".%s" % MAKEFILE_EXTENSION
):
180 pkg
= packages
.Makefile(self
.pakfire
, filename
)
181 filename
= pkg
.dist(os
.path
.join(self
.resultdir
, "src"))
183 assert os
.path
.exists(filename
), filename
185 # Open source package.
186 self
.pkg
= packages
.SourcePackage(self
.pakfire
, None, filename
)
187 assert self
.pkg
, filename
189 # Log the package information.
190 self
.log
.info(_("Package information:"))
191 for line
in self
.pkg
.dump(long=True).splitlines():
192 self
.log
.info(" %s" % line
)
195 # Path where we extract the package and put all the source files.
196 self
.build_dir
= os
.path
.join(self
.path
, "usr/src/packages", self
.pkg
.friendly_name
)
201 # XXX need to make this configureable
203 "enable_loop_devices" : True,
204 "enable_ccache" : True,
205 "enable_icecream" : False,
206 "sign_packages" : False,
208 #self.settings.update(settings)
210 # Try to get the configured host key. If it is available,
211 # we will automatically sign all packages with it.
212 if self
.keyring
.get_host_key(secret
=True):
213 self
.settings
["sign_packages"] = True
219 # Save the build time.
220 self
.build_time
= int(time
.time())
222 def setup_signal_handlers(self
):
226 # Mount the directories.
232 # Setup domain name resolution in chroot.
235 # Extract all needed packages.
240 # Kill all still running processes in the cgroup.
241 self
.cgroup
.kill_and_wait()
243 # Remove cgroup and all parent cgroups if they are empty.
244 self
.cgroup
.migrate_task(self
.cgroup
.root
, os
.getpid())
245 self
.cgroup
.destroy()
247 parent
= self
.cgroup
.parent
249 if not parent
.is_empty(recursive
=True):
253 parent
= parent
.parent
256 util
.orphans_kill(self
.path
)
258 # Close pakfire instance.
261 # Umount the build environment.
270 Inherit architecture from distribution configuration.
272 return self
.distro
.arch
277 "build_date" : time
.strftime("%a, %d %b %Y %H:%M:%S +0000", time
.gmtime(self
.build_time
)),
278 "build_host" : socket
.gethostname(),
279 "build_id" : self
.build_id
,
280 "build_time" : self
.build_time
,
286 Shortcut to access the pakfire keyring.
288 return self
.pakfire
.keyring
291 filename
= os
.path
.join(self
.path
, ".lock")
294 self
._lock
= open(filename
, "a+")
299 fcntl
.lockf(self
._lock
.fileno(), fcntl
.LOCK_EX | fcntl
.LOCK_NB
)
301 raise BuildRootLocked
, "Buildroot is locked"
310 def copyin(self
, file_out
, file_in
):
311 if file_in
.startswith("/"):
312 file_in
= file_in
[1:]
314 file_in
= self
.chrootPath(file_in
)
316 #if not os.path.exists(file_out):
319 dir_in
= os
.path
.dirname(file_in
)
320 if not os
.path
.exists(dir_in
):
323 self
.log
.debug("%s --> %s" % (file_out
, file_in
))
325 shutil
.copy2(file_out
, file_in
)
327 def copyout(self
, file_in
, file_out
):
328 if file_in
.startswith("/"):
329 file_in
= file_in
[1:]
331 file_in
= self
.chrootPath(file_in
)
333 #if not os.path.exists(file_in):
336 dir_out
= os
.path
.dirname(file_out
)
337 if not os
.path
.exists(dir_out
):
340 self
.log
.debug("%s --> %s" % (file_in
, file_out
))
342 shutil
.copy2(file_in
, file_out
)
344 def copy_result(self
, resultdir
):
345 # XXX should use find_result_packages
347 dir_in
= self
.chrootPath("result")
349 for dir, subdirs
, files
in os
.walk(dir_in
):
350 basename
= os
.path
.basename(dir)
351 dir = dir[len(self
.chrootPath()):]
353 file_in
= os
.path
.join(dir, file)
355 file_out
= os
.path
.join(
361 self
.copyout(file_in
, file_out
)
363 def find_result_packages(self
):
366 for dir, subdirs
, files
in os
.walk(self
.resultdir
):
368 if not file.endswith(".%s" % PACKAGE_EXTENSION
):
371 file = os
.path
.join(dir, file)
376 def extract(self
, requires
=None, build_deps
=True):
378 Gets a dependency set and extracts all packages
384 if self
.use_cache
and os
.path
.exists(self
.cache_file
):
385 # If we are told to use the cache, we just import the
389 # Add neccessary build dependencies.
390 requires
+= BUILD_PACKAGES
392 # If we have ccache enabled, we need to extract it
393 # to the build chroot.
394 if self
.settings
.get("enable_ccache"):
395 requires
.append("ccache")
397 # If we have icecream enabled, we need to extract it
398 # to the build chroot.
399 if self
.settings
.get("enable_icecream"):
400 requires
.append("icecream")
402 # Get build dependencies from source package.
404 for req
in self
.pkg
.requires
:
407 # Install all packages.
408 self
.log
.info(_("Install packages needed for build..."))
409 self
.install(requires
)
411 # Copy the makefile and load source tarballs.
413 self
.pkg
.extract(_("Extracting"), prefix
=self
.build_dir
)
415 def install(self
, requires
, **kwargs
):
417 Install everything that is required in requires.
419 # If we got nothing to do, we quit immediately.
424 "interactive" : False,
428 if not kwargs
.has_key("allow_downgrade"):
429 kwargs
["allow_downgrade"] = True
431 # Install everything.
432 self
.pakfire
.install(requires
, **kwargs
)
434 def chrootPath(self
, *args
):
435 # Remove all leading slashes
438 if arg
.startswith("/"):
443 ret
= os
.path
.join(self
.path
, *args
)
444 ret
= ret
.replace("//", "/")
446 assert ret
.startswith(self
.path
)
450 def populate_dev(self
):
464 # If we need loop devices (which are optional) we create them here.
465 if self
.settings
["enable_loop_devices"]:
466 for i
in range(0, 7):
467 nodes
.append("/dev/loop%d" % i
)
470 # Stat the original node of the host system and copy it to
472 node_stat
= os
.stat(node
)
474 self
._create
_node
(node
, node_stat
.st_mode
, node_stat
.st_rdev
)
476 os
.symlink("/proc/self/fd/0", self
.chrootPath("dev", "stdin"))
477 os
.symlink("/proc/self/fd/1", self
.chrootPath("dev", "stdout"))
478 os
.symlink("/proc/self/fd/2", self
.chrootPath("dev", "stderr"))
479 os
.symlink("/proc/self/fd", self
.chrootPath("dev", "fd"))
483 Add DNS resolution facility to chroot environment by copying
484 /etc/resolv.conf and /etc/hosts.
486 for i
in ("/etc/resolv.conf", "/etc/hosts"):
489 def _create_node(self
, filename
, mode
, device
):
490 self
.log
.debug("Create node: %s (%s)" % (filename
, mode
))
492 filename
= self
.chrootPath(filename
)
494 # Create parent directory if it is missing.
495 dirname
= os
.path
.dirname(filename
)
496 if not os
.path
.exists(dirname
):
499 os
.mknod(filename
, mode
, device
)
502 self
.log
.debug("Destroying environment %s" % self
.path
)
504 if os
.path
.exists(self
.path
):
508 self
.log
.debug("Cleaning environemnt.")
510 # Remove the build directory and buildroot.
511 dirs
= (self
.build_dir
, self
.chrootPath("result"),)
514 if not os
.path
.exists(d
):
521 self
.log
.debug("Mounting environment")
522 for src
, dest
, fs
, options
in self
.mountpoints
:
523 mountpoint
= self
.chrootPath(dest
)
525 options
= "-o %s" % options
527 # Eventually create mountpoint directory
528 if not os
.path
.exists(mountpoint
):
529 os
.makedirs(mountpoint
)
531 cmd
= "mount -n -t %s %s %s %s" % \
532 (fs
, options
, src
, mountpoint
)
533 chroot
.do(cmd
, shell
=True)
535 def _umountall(self
):
536 self
.log
.debug("Umounting environment")
539 for src
, dest
, fs
, options
in reversed(self
.mountpoints
):
540 if not dest
in mountpoints
:
541 mountpoints
.append(dest
)
543 for dest
in mountpoints
:
544 mountpoint
= self
.chrootPath(dest
)
546 chroot
.do("umount -n %s" % mountpoint
, raiseExc
=0, shell
=True)
549 def mountpoints(self
):
552 # Make root as a tmpfs.
554 # ("pakfire_root", "/", "tmpfs", "defaults"),
558 # src, dest, fs, options
559 ("pakfire_proc", "/proc", "proc", "nosuid,noexec,nodev"),
560 ("/proc/sys", "/proc/sys", "bind", "bind"),
561 ("/proc/sys", "/proc/sys", "bind", "bind,ro,remount"),
562 ("/sys", "/sys", "bind", "bind"),
563 ("/sys", "/sys", "bind", "bind,ro,remount"),
564 ("pakfire_tmpfs", "/dev", "tmpfs", "mode=755,nosuid"),
565 ("/dev/pts", "/dev/pts", "bind", "bind"),
566 ("pakfire_tmpfs", "/run", "tmpfs", "mode=755,nosuid,nodev"),
569 # If selinux is enabled.
570 if os
.path
.exists("/sys/fs/selinux"):
572 ("/sys/fs/selinux", "/sys/fs/selinux", "bind", "bind"),
573 ("/sys/fs/selinux", "/sys/fs/selinux", "bind", "bind,ro,remount"),
576 # If ccache support is requested, we bind mount the cache.
577 if self
.settings
.get("enable_ccache"):
578 # Create ccache cache directory if it does not exist.
579 if not os
.path
.exists(CCACHE_CACHE_DIR
):
580 os
.makedirs(CCACHE_CACHE_DIR
)
583 (CCACHE_CACHE_DIR
, "/var/cache/ccache", "bind", "bind"),
591 # Add HOME manually, because it is occasionally not set
592 # and some builds get in trouble then.
593 "PATH" : "/usr/bin:/bin:/usr/sbin:/sbin",
595 "TERM" : os
.environ
.get("TERM", "vt100"),
599 "LANG" : os
.environ
.setdefault("LANG", "en_US.UTF-8"),
601 # Set the container that we can detect, if we are inside a
603 "container" : "pakfire-builder",
606 # Inherit environment from distro
607 env
.update(self
.pakfire
.distro
.environ
)
609 # Icecream environment settings
610 if self
.settings
.get("enable_icecream", False):
611 # Set the toolchain path
612 if self
.settings
.get("icecream_toolchain", None):
613 env
["ICECC_VERSION"] = self
.settings
.get("icecream_toolchain")
615 # Set preferred host if configured.
616 if self
.settings
.get("icecream_preferred_host", None):
617 env
["ICECC_PREFERRED_HOST"] = \
618 self
.settings
.get("icecream_preferred_host")
620 # Fake UTS_MACHINE, when we cannot use the personality syscall and
621 # if the host architecture is not equal to the target architecture.
622 if not self
.pakfire
.distro
.personality
and \
623 not system
.native_arch
== self
.pakfire
.distro
.arch
:
625 "LD_PRELOAD" : "/usr/lib/libpakfire_preload.so",
626 "UTS_MACHINE" : self
.pakfire
.distro
.arch
,
632 def installed_packages(self
):
634 Returns an iterator over all installed packages in this build environment.
636 # Get the repository of all installed packages.
637 repo
= self
.pakfire
.repos
.get_repo("@system")
639 # Return an iterator over the packages.
642 def write_config(self
):
643 # Cleanup everything in /etc/pakfire.
644 util
.rm(self
.chrootPath(CONFIG_DIR
))
646 for i
in (CONFIG_DIR
, CONFIG_REPOS_DIR
):
647 i
= self
.chrootPath(i
)
648 if not os
.path
.exists(i
):
651 # Write general.conf.
652 f
= open(self
.chrootPath(CONFIG_DIR
, "general.conf"), "w")
655 # Write builder.conf.
656 f
= open(self
.chrootPath(CONFIG_DIR
, "builder.conf"), "w")
657 f
.write(self
.distro
.get_config())
660 # Create pakfire configuration files.
661 for repo
in self
.pakfire
.repos
:
662 conf
= repo
.get_config()
667 filename
= self
.chrootPath(CONFIG_REPOS_DIR
, "%s.repo" % repo
.name
)
668 f
= open(filename
, "w")
669 f
.write("\n".join(conf
))
672 def do(self
, command
, shell
=True, personality
=None, logger
=None, *args
, **kwargs
):
675 # Environment variables
678 if kwargs
.has_key("env"):
679 env
.update(kwargs
.pop("env"))
681 self
.log
.debug("Environment:")
682 for k
, v
in sorted(env
.items()):
683 self
.log
.debug(" %s=%s" % (k
, v
))
685 # Update personality it none was set
687 personality
= self
.distro
.personality
689 # Make every shell to a login shell because we set a lot of
690 # environment things there.
692 command
= ["bash", "--login", "-c", command
]
694 if not kwargs
.has_key("chrootPath"):
695 kwargs
["chrootPath"] = self
.chrootPath()
697 if not kwargs
.has_key("cgroup"):
698 kwargs
["cgroup"] = self
.cgroup
702 personality
=personality
,
712 def build(self
, install_test
=True):
714 raise BuildError
, _("You cannot run a build when no package was given.")
716 # Search for the package file in build_dir and raise BuildError if it is not present.
717 pkgfile
= os
.path
.join(self
.build_dir
, "%s.%s" % (self
.pkg
.name
, MAKEFILE_EXTENSION
))
718 if not os
.path
.exists(pkgfile
):
719 raise BuildError
, _("Could not find makefile in build root: %s") % pkgfile
720 pkgfile
= "/%s" % os
.path
.relpath(pkgfile
, self
.chrootPath())
722 # Write pakfire configuration into the chroot.
725 # Create the build command, that is executed in the chroot.
727 "/usr/lib/pakfire/builder",
733 "--resultdir=/result",
737 self
.do(" ".join(build_command
), logger
=self
.log
)
739 # Perform the install test after the actual build.
744 self
.log
.error(_("Build failed."), exc_info
=True)
746 raise BuildError
, _("The build command failed. See logfile for details.")
748 # Sign all built packages with the host key (if available).
749 if self
.settings
.get("sign_packages"):
750 host_key
= self
.keyring
.get_host_key_id()
756 # Dump package information.
759 def install_test(self
):
760 self
.log
.info(_("Running installation test..."))
762 # Install all packages that were built.
763 self
.install(self
.find_result_packages(),
764 allow_vendorchange
=True, allow_archchange
=True,
765 allow_uninstall
=True, signatures_mode
="disabled")
767 self
.log
.info(_("Installation test succeeded."))
770 def shell(self
, args
=[]):
771 if not util
.cli_is_interactive():
772 self
.log
.warning("Cannot run shell on non-interactive console.")
775 # Install all packages that are needed to run a shell.
776 self
.install(SHELL_PACKAGES
)
778 # XXX need to set CFLAGS here
779 command
= "/usr/sbin/chroot %s %s %s" % \
780 (self
.chrootPath(), SHELL_SCRIPT
, " ".join(args
))
782 # Add personality if we require one
783 if self
.pakfire
.distro
.personality
:
784 command
= "%s %s" % (self
.pakfire
.distro
.personality
, command
)
786 for key
, val
in self
.environ
.items():
787 command
= "%s=\"%s\" " % (key
, val
) + command
789 # Empty the environment
790 command
= "env -i - %s" % command
792 self
.log
.debug("Shell command: %s" % command
)
794 shell
= os
.system(command
)
795 return os
.WEXITSTATUS(shell
)
797 def sign(self
, keyfp
):
798 assert self
.keyring
.get_key(keyfp
), "Key for signing does not exist"
800 # Find all files to process.
801 files
= self
.find_result_packages()
803 # Create a progressbar.
804 print _("Signing packages...")
805 p
= util
.make_progress(keyfp
, len(files
))
809 # Update progressbar.
815 pkg
= packages
.open(self
.pakfire
, None, file)
823 print "" # Print an empty line.
828 for file in self
.find_result_packages():
829 pkg
= packages
.open(self
.pakfire
, None, file)
832 # If there are no packages, there is nothing to do.
838 self
.log
.info(_("Dumping package information:"))
840 dump
= pkg
.dump(long=True)
842 for line
in dump
.splitlines():
843 self
.log
.info(" %s" % line
)
844 self
.log
.info("") # Empty line.
847 def cache_file(self
):
849 self
.pakfire
.distro
.sname
, # name of the distribution
850 self
.pakfire
.distro
.release
, # release version
851 self
.pakfire
.distro
.arch
, # architecture
854 return os
.path
.join(CACHE_ENVIRON_DIR
, "%s.cache" %"-".join(comps
))
856 def cache_export(self
, filename
):
857 # Sync all disk caches.
860 # A list to store all mountpoints, so we don't package them.
863 # A list containing all files we want to package.
866 # Walk through the whole tree and collect all files
867 # that are on the same disk (not crossing mountpoints).
868 log
.info(_("Creating filelist..."))
869 root
= self
.chrootPath()
870 for dir, subdirs
, files
in os
.walk(root
):
871 # Search for mountpoints and skip them.
872 if not dir == root
and os
.path
.ismount(dir):
873 mountpoints
.append(dir)
876 # Skip all directories under mountpoints.
877 if any([dir.startswith(m
) for m
in mountpoints
]):
880 # Add all other files.
883 file = os
.path
.join(dir, file)
884 filelist
.append(file)
886 # Create a nice progressbar.
887 p
= util
.make_progress(_("Compressing files..."), len(filelist
))
890 # Create tar file and add all files to it.
891 f
= packages
.file.InnerTarFile
.open(filename
, "w:gz")
892 for file in filelist
:
897 f
.add(file, os
.path
.relpath(file, root
), recursive
=False)
900 # Finish progressbar.
904 filesize
= os
.path
.getsize(filename
)
906 log
.info(_("Cache file was successfully created at %s.") % filename
)
907 log
.info(_(" Containing %(files)s files, it has a size of %(size)s.") % \
908 { "files" : len(filelist
), "size" : util
.format_size(filesize
), })
910 def cache_extract(self
):
911 root
= self
.chrootPath()
912 filename
= self
.cache_file
914 f
= packages
.file.InnerTarFile
.open(filename
, "r:gz")
915 members
= f
.getmembers()
917 # Make a nice progress bar as always.
918 p
= util
.make_progress(_("Extracting files..."), len(members
))
920 # Extract all files from the cache.
922 for member
in members
:
927 f
.extract(member
, path
=root
)
930 # Finish progressbar.
934 # Re-read local repository.
935 self
.pakfire
.repos
.local
.update(force
=True)
937 # Update all packages.
938 self
.log
.info(_("Updating packages from cache..."))
939 self
.pakfire
.update(interactive
=False, logger
=self
.log
,
940 allow_archchange
=True, allow_vendorchange
=True, allow_downgrade
=True)
943 class Builder(object):
944 def __init__(self
, pakfire
, filename
, resultdir
, **kwargs
):
945 self
.pakfire
= pakfire
947 self
.filename
= filename
949 self
.resultdir
= resultdir
952 self
.pkg
= packages
.Makefile(self
.pakfire
, self
.filename
)
960 return self
.pkg
.buildroot
964 return self
.pakfire
.distro
970 # Get all definitions from the package.
971 environ
.update(self
.pkg
.exports
)
973 # Overwrite some definitions by default values.
974 environ
.update(self
._environ
)
978 def do(self
, command
, shell
=True, *args
, **kwargs
):
980 logger
= kwargs
["logger"]
982 logger
= logging
.getLogger("pakfire")
983 kwargs
["logger"] = logger
985 # Environment variables
986 log
.debug("Environment:")
987 for k
, v
in sorted(self
.environ
.items()):
988 log
.debug(" %s=%s" % (k
, v
))
990 # Update personality it none was set
991 if not kwargs
.has_key("personality"):
992 kwargs
["personality"] = self
.distro
.personality
994 if not kwargs
.has_key("cwd"):
995 kwargs
["cwd"] = "/%s" % LOCAL_TMP_PATH
997 # Make every shell to a login shell because we set a lot of
998 # environment things there.
1000 command
= ["bash", "--login", "-c", command
]
1001 kwargs
["shell"] = False
1003 kwargs
["env"] = self
.environ
1006 return chroot
.do(command
, *args
, **kwargs
)
1009 logger
= logging
.getLogger("pakfire")
1011 logger
.error("Command exited with an error: %s" % command
)
1014 def run_script(self
, script
, *args
):
1015 if not script
.startswith("/"):
1016 script
= os
.path
.join(SCRIPT_DIR
, script
)
1018 assert os
.path
.exists(script
), "Script we should run does not exist: %s" % script
1025 # Returns the output of the command, but the output won't get
1027 return self
.do(cmd
, returnOutput
=True, logger
=None)
1029 def create_icecream_toolchain(self
):
1031 out
= self
.do("icecc --build-native 2>/dev/null", returnOutput
=True, cwd
="/tmp")
1035 for line
in out
.splitlines():
1036 m
= re
.match(r
"^creating ([a-z0-9]+\.tar\.gz)", line
)
1038 self
._environ
["ICECC_VERSION"] = "/tmp/%s" % m
.group(1)
1040 def create_buildscript(self
, stage
):
1041 # Get buildscript from the package.
1042 script
= self
.pkg
.get_buildscript(stage
)
1044 # Write script to an empty file.
1045 f
= tempfile
.NamedTemporaryFile(mode
="w", delete
=False)
1046 f
.write("#!/bin/sh\n\n")
1049 f
.write("\n%s\n" % script
)
1053 # Make the script executable.
1054 os
.chmod(f
.name
, 700)
1059 # Create buildroot and remove all content if it was existant.
1060 util
.rm(self
.buildroot
)
1061 os
.makedirs(self
.buildroot
)
1063 # Build icecream toolchain if icecream is installed.
1064 self
.create_icecream_toolchain()
1066 for stage
in ("prepare", "build", "test", "install"):
1067 self
.build_stage(stage
)
1069 # Run post-build stuff.
1070 self
.post_compress_man_pages()
1071 self
.post_remove_static_libs()
1072 self
.post_extract_debuginfo()
1074 # Package the result.
1075 # Make all these little package from the build environment.
1076 log
.info(_("Creating packages:"))
1078 for pkg
in reversed(self
.pkg
.packages
):
1079 packager
= packages
.packager
.BinaryPackager(self
.pakfire
, pkg
,
1080 self
, self
.buildroot
)
1081 pkg
= packager
.run(self
.resultdir
)
1085 def build_stage(self
, stage
):
1086 # Get the buildscript for this stage.
1087 buildscript
= self
.create_buildscript(stage
)
1089 # Execute the buildscript of this stage.
1090 log
.info(_("Running stage %s:") % stage
)
1093 self
.do(buildscript
, shell
=False)
1096 # Remove the buildscript.
1097 if os
.path
.exists(buildscript
):
1098 os
.unlink(buildscript
)
1100 def post_remove_static_libs(self
):
1101 keep_libs
= self
.pkg
.lexer
.build
.get_var("keep_libraries")
1102 keep_libs
= keep_libs
.split()
1105 self
.do("%s/remove-static-libs %s %s" % \
1106 (SCRIPT_DIR
, self
.buildroot
, " ".join(keep_libs
)))
1108 log
.warning(_("Could not remove static libraries: %s") % e
)
1110 def post_compress_man_pages(self
):
1112 self
.do("%s/compress-man-pages %s" % (SCRIPT_DIR
, self
.buildroot
))
1114 log
.warning(_("Compressing man pages did not complete successfully."))
1116 def post_extract_debuginfo(self
):
1119 # Check if we need to run with strict build-id.
1120 strict_id
= self
.pkg
.lexer
.build
.get_var("debuginfo_strict_build_id", "true")
1121 if strict_id
in ("true", "yes", "1"):
1122 args
.append("--strict-build-id")
1124 args
.append("--buildroot=%s" % self
.pkg
.buildroot
)
1125 args
.append("--sourcedir=%s" % self
.pkg
.sourcedir
)
1127 # Get additional options to pass to script.
1128 options
= self
.pkg
.lexer
.build
.get_var("debuginfo_options", "")
1129 args
+= options
.split()
1132 self
.do("%s/extract-debuginfo %s %s" % (SCRIPT_DIR
, " ".join(args
), self
.pkg
.buildroot
))
1134 log
.error(_("Extracting debuginfo did not complete with success. Aborting build."))
1137 def find_prerequires(self
, scriptlet_file
):
1138 assert os
.path
.exists(scriptlet_file
), "Scriptlet file does not exist: %s" % scriptlet_file
1140 res
= self
.run_script("find-prerequires", scriptlet_file
)
1141 prerequires
= set(res
.splitlines())
1146 if os
.path
.exists(self
.buildroot
):
1147 util
.rm(self
.buildroot
)