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 ###############################################################################
39 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
, pakfire
, filename
=None, distro_name
=None, build_id
=None, logfile
=None, release_build
=True, **kwargs
):
73 self
.pakfire
= pakfire
75 # Check if the given pakfire instance is of the correct type.
76 assert isinstance(self
.pakfire
, base
.PakfireBuilder
)
78 # Check if this host can build the requested architecture.
79 if not system
.host_supports_arch(self
.arch
):
80 raise BuildError
, _("Cannot build for %s on this host.") % self
.arch
82 # Save the build id and generate one if no build id was provided.
84 build_id
= "%s" % uuid
.uuid4()
86 self
.build_id
= build_id
89 self
.init_logging(logfile
)
91 # Initialize a cgroup (if supported).
94 # This build is a release build?
95 self
.release_build
= release_build
97 if self
.release_build
:
98 # Disable the local build repository in release mode.
99 self
.pakfire
.repos
.disable_repo("build")
101 # Log information about pakfire and some more information, when we
102 # are running in release mode.
104 "host_arch" : system
.arch
,
105 "hostname" : system
.hostname
,
106 "time" : time
.strftime("%a, %d %b %Y %H:%M:%S +0000", time
.gmtime()),
107 "version" : "Pakfire %s" % PAKFIRE_VERSION
,
110 for line
in BUILD_LOG_HEADER
.splitlines():
111 self
.log
.info(line
% logdata
)
115 "enable_loop_devices" : self
.config
.get_bool("builder", "use_loop_devices", True),
116 "enable_ccache" : self
.config
.get_bool("builder", "use_ccache", True),
117 "enable_icecream" : self
.config
.get_bool("builder", "use_icecream", False),
118 "sign_packages" : False,
119 "buildroot_tmpfs" : self
.config
.get_bool("builder", "use_tmpfs", False),
120 "private_network" : self
.config
.get_bool("builder", "private_network", False),
123 # Get ccache settings.
124 if self
.settings
.get("enable_ccache", False):
125 self
.settings
.update({
126 "ccache_compress" : self
.config
.get_bool("ccache", "compress", True),
129 # Try to get the configured host key. If it is available,
130 # we will automatically sign all packages with it.
131 if self
.keyring
.get_host_key(secret
=True):
132 self
.settings
["sign_packages"] = True
134 # Add settings from keyword arguments.
135 self
.settings
.update(kwargs
)
137 # Where do we put the result?
138 self
.resultdir
= os
.path
.join(self
.pakfire
.path
, "result")
141 # If we have a plain makefile, we first build a source package and go with that.
143 # Open source package.
144 self
.pkg
= packages
.SourcePackage(self
.pakfire
, None, filename
)
145 assert self
.pkg
, filename
147 # Log the package information.
148 self
.log
.info(_("Package information:"))
149 for line
in self
.pkg
.dump(long=True).splitlines():
150 self
.log
.info(" %s" % line
)
153 # Path where we extract the package and put all the source files.
154 self
.build_dir
= os
.path
.join(self
.path
, "usr/src/packages", self
.pkg
.friendly_name
)
162 # Save the build time.
163 self
.build_time
= time
.time()
165 def setup_signal_handlers(self
):
169 assert not self
.pakfire
.initialized
, "Pakfire has already been initialized"
172 # If this fails because the kernel has no support for CLONE_NEWIPC or CLONE_NEWUTS,
173 # we try to fall back to just set CLONE_NEWNS.
175 _pakfire
.unshare(_pakfire
.SCHED_CLONE_NEWNS|_pakfire
.SCHED_CLONE_NEWIPC|_pakfire
.SCHED_CLONE_NEWUTS
)
176 except RuntimeError, e
:
177 _pakfire
.unshare(_pakfire
.SCHED_CLONE_NEWNS
)
179 # Mount the directories.
182 # Lock the build environment.
185 # Initialize pakfire instance.
186 self
.pakfire
.initialize()
188 # Optionally enable private networking.
189 if self
.settings
.get("private_network", None):
190 _pakfire
.unshare(_pakfire
.SCHED_CLONE_NEWNET
)
195 # Setup domain name resolution in chroot.
198 # Extract all needed packages.
203 # Kill all still running processes in the cgroup.
204 self
.cgroup
.kill_and_wait()
206 # Remove cgroup and all parent cgroups if they are empty.
207 self
.cgroup
.migrate_task(self
.cgroup
.root
, os
.getpid())
208 self
.cgroup
.destroy()
210 parent
= self
.cgroup
.parent
212 if not parent
.is_empty(recursive
=True):
216 parent
= parent
.parent
219 util
.orphans_kill(self
.path
)
221 # Shut down pakfire instance.
222 self
.pakfire
.destroy()
224 # Unlock build environment.
227 # Umount the build environment.
236 Proxy method for easy access to the configuration.
238 return self
.pakfire
.config
243 Proxy method for easy access to the distribution.
245 return self
.pakfire
.distro
250 Proxy method for easy access to the path.
252 return self
.pakfire
.path
257 Inherit architecture from distribution configuration.
259 return self
.pakfire
.distro
.arch
262 def personality(self
):
264 Gets the personality from the distribution configuration.
266 return self
.pakfire
.distro
.personality
271 "build_date" : time
.strftime("%a, %d %b %Y %H:%M:%S +0000", time
.gmtime(self
.build_time
)),
272 "build_host" : socket
.gethostname(),
273 "build_id" : self
.build_id
,
274 "build_time" : self
.build_time
,
280 Shortcut to access the pakfire keyring.
282 return self
.pakfire
.keyring
285 filename
= os
.path
.join(self
.path
, ".lock")
288 self
._lock
= open(filename
, "a+")
293 fcntl
.lockf(self
._lock
.fileno(), fcntl
.LOCK_EX | fcntl
.LOCK_NB
)
295 raise BuildRootLocked
, "Buildroot is locked"
304 def init_cgroup(self
):
306 Initialize cgroup (if the system supports it).
308 if not cgroup
.supported():
312 # Search for the cgroup this process is currently running in.
313 parent_cgroup
= cgroup
.find_by_pid(os
.getpid())
314 if not parent_cgroup
:
317 # Create our own cgroup inside the parent cgroup.
318 self
.cgroup
= parent_cgroup
.create_child_cgroup("pakfire/builder/%s" % self
.build_id
)
320 # Attach the pakfire-builder process to the group.
323 def init_logging(self
, logfile
):
325 self
.log
= log
.getChild(self
.build_id
)
326 # Propage everything to the root logger that we will see something
328 self
.log
.propagate
= 1
329 self
.log
.setLevel(logging
.INFO
)
331 # Add the given logfile to the logger.
332 h
= logging
.FileHandler(logfile
)
333 self
.log
.addHandler(h
)
335 # Format the log output for the file.
336 f
= logger
.BuildFormatter()
339 # If no logile was given, we use the root logger.
340 self
.log
= logging
.getLogger("pakfire")
342 def copyin(self
, file_out
, file_in
):
343 if file_in
.startswith("/"):
344 file_in
= file_in
[1:]
346 file_in
= self
.chrootPath(file_in
)
348 #if not os.path.exists(file_out):
351 dir_in
= os
.path
.dirname(file_in
)
352 if not os
.path
.exists(dir_in
):
355 self
.log
.debug("%s --> %s" % (file_out
, file_in
))
357 shutil
.copy2(file_out
, file_in
)
359 def copyout(self
, file_in
, file_out
):
360 if file_in
.startswith("/"):
361 file_in
= file_in
[1:]
363 file_in
= self
.chrootPath(file_in
)
365 #if not os.path.exists(file_in):
368 dir_out
= os
.path
.dirname(file_out
)
369 if not os
.path
.exists(dir_out
):
372 self
.log
.debug("%s --> %s" % (file_in
, file_out
))
374 shutil
.copy2(file_in
, file_out
)
376 def copy_result(self
, resultdir
):
377 # XXX should use find_result_packages
379 dir_in
= self
.chrootPath("result")
381 for dir, subdirs
, files
in os
.walk(dir_in
):
382 basename
= os
.path
.basename(dir)
383 dir = dir[len(self
.chrootPath()):]
385 file_in
= os
.path
.join(dir, file)
387 file_out
= os
.path
.join(
393 self
.copyout(file_in
, file_out
)
395 def find_result_packages(self
):
398 for dir, subdirs
, files
in os
.walk(self
.resultdir
):
400 if not file.endswith(".%s" % PACKAGE_EXTENSION
):
403 file = os
.path
.join(dir, file)
408 def extract(self
, requires
=None):
410 Gets a dependency set and extracts all packages
416 # Add neccessary build dependencies.
417 requires
+= BUILD_PACKAGES
419 # If we have ccache enabled, we need to extract it
420 # to the build chroot.
421 if self
.settings
.get("enable_ccache"):
422 requires
.append("ccache")
424 # If we have icecream enabled, we need to extract it
425 # to the build chroot.
426 if self
.settings
.get("enable_icecream"):
427 requires
.append("icecream")
429 # Get build dependencies from source package.
431 for req
in self
.pkg
.requires
:
434 # Install all packages.
435 self
.log
.info(_("Install packages needed for build..."))
436 self
.install(requires
)
438 # Copy the makefile and load source tarballs.
440 self
.pkg
.extract(_("Extracting"), prefix
=self
.build_dir
)
442 # Add an empty line at the end.
445 def install(self
, requires
, **kwargs
):
447 Install everything that is required in requires.
449 # If we got nothing to do, we quit immediately.
454 "interactive" : False,
458 if not kwargs
.has_key("allow_downgrade"):
459 kwargs
["allow_downgrade"] = True
461 # Install everything.
462 self
.pakfire
.install(requires
, **kwargs
)
464 def chrootPath(self
, *args
):
465 # Remove all leading slashes
468 if arg
.startswith("/"):
473 ret
= os
.path
.join(self
.path
, *args
)
474 ret
= ret
.replace("//", "/")
476 assert ret
.startswith(self
.path
)
480 def populate_dev(self
):
494 # If we need loop devices (which are optional) we create them here.
495 if self
.settings
["enable_loop_devices"]:
496 for i
in range(0, 7):
497 nodes
.append("/dev/loop%d" % i
)
500 # Stat the original node of the host system and copy it to
502 node_stat
= os
.stat(node
)
504 self
._create
_node
(node
, node_stat
.st_mode
, node_stat
.st_rdev
)
506 os
.symlink("/proc/self/fd/0", self
.chrootPath("dev", "stdin"))
507 os
.symlink("/proc/self/fd/1", self
.chrootPath("dev", "stdout"))
508 os
.symlink("/proc/self/fd/2", self
.chrootPath("dev", "stderr"))
509 os
.symlink("/proc/self/fd", self
.chrootPath("dev", "fd"))
513 Add DNS resolution facility to chroot environment by copying
514 /etc/resolv.conf and /etc/hosts.
516 for i
in ("/etc/resolv.conf", "/etc/hosts"):
519 def _create_node(self
, filename
, mode
, device
):
520 self
.log
.debug("Create node: %s (%s)" % (filename
, mode
))
522 filename
= self
.chrootPath(filename
)
524 # Create parent directory if it is missing.
525 dirname
= os
.path
.dirname(filename
)
526 if not os
.path
.exists(dirname
):
529 os
.mknod(filename
, mode
, device
)
532 self
.log
.debug("Destroying environment %s" % self
.path
)
534 if os
.path
.exists(self
.path
):
538 self
.log
.debug("Cleaning environemnt.")
540 # Remove the build directory and buildroot.
541 dirs
= (self
.build_dir
, self
.chrootPath("result"),)
544 if not os
.path
.exists(d
):
551 self
.log
.debug("Mounting environment")
552 for src
, dest
, fs
, options
in self
.mountpoints
:
553 mountpoint
= self
.chrootPath(dest
)
555 options
= "-o %s" % options
557 # Eventually create mountpoint directory
558 if not os
.path
.exists(mountpoint
):
559 os
.makedirs(mountpoint
)
561 self
.execute_root("mount -n -t %s %s %s %s" % (fs
, options
, src
, mountpoint
), shell
=True)
563 def _umountall(self
):
564 self
.log
.debug("Umounting environment")
567 for src
, dest
, fs
, options
in reversed(self
.mountpoints
):
568 dest
= self
.chrootPath(dest
)
570 if not dest
in mountpoints
:
571 mountpoints
.append(dest
)
574 for mp
in mountpoints
:
576 self
.execute_root("umount -n %s" % mp
, shell
=True)
577 except ShellEnvironmentError
:
580 if not os
.path
.ismount(mp
):
581 mountpoints
.remove(mp
)
584 def mountpoints(self
):
587 # Make root as a tmpfs if enabled.
588 if self
.settings
.get("buildroot_tmpfs"):
590 ("pakfire_root", "/", "tmpfs", "defaults"),
594 # src, dest, fs, options
595 ("pakfire_proc", "/proc", "proc", "nosuid,noexec,nodev"),
596 ("/proc/sys", "/proc/sys", "bind", "bind"),
597 ("/proc/sys", "/proc/sys", "bind", "bind,ro,remount"),
598 ("/sys", "/sys", "bind", "bind"),
599 ("/sys", "/sys", "bind", "bind,ro,remount"),
600 ("pakfire_tmpfs", "/dev", "tmpfs", "mode=755,nosuid"),
601 ("/dev/pts", "/dev/pts", "bind", "bind"),
602 ("pakfire_tmpfs", "/run", "tmpfs", "mode=755,nosuid,nodev"),
603 ("pakfire_tmpfs", "/tmp", "tmpfs", "mode=755,nosuid,nodev"),
606 # If selinux is enabled.
607 if os
.path
.exists("/sys/fs/selinux"):
609 ("/sys/fs/selinux", "/sys/fs/selinux", "bind", "bind"),
610 ("/sys/fs/selinux", "/sys/fs/selinux", "bind", "bind,ro,remount"),
613 # If ccache support is requested, we bind mount the cache.
614 if self
.settings
.get("enable_ccache"):
615 # Create ccache cache directory if it does not exist.
616 if not os
.path
.exists(CCACHE_CACHE_DIR
):
617 os
.makedirs(CCACHE_CACHE_DIR
)
620 (CCACHE_CACHE_DIR
, "/var/cache/ccache", "bind", "bind"),
628 # Add HOME manually, because it is occasionally not set
629 # and some builds get in trouble then.
630 "PATH" : "/usr/bin:/bin:/usr/sbin:/sbin",
632 "TERM" : os
.environ
.get("TERM", "vt100"),
636 "LANG" : os
.environ
.setdefault("LANG", "en_US.UTF-8"),
638 # Set the container that we can detect, if we are inside a
640 "container" : "pakfire-builder",
643 # Inherit environment from distro
644 env
.update(self
.pakfire
.distro
.environ
)
646 # ccache environment settings
647 if self
.settings
.get("enable_ccache", False):
648 compress
= self
.settings
.get("ccache_compress", False)
650 env
["CCACHE_COMPRESS"] = "1"
652 # Let ccache create its temporary files in /tmp.
653 env
["CCACHE_TEMPDIR"] = "/tmp"
655 # Icecream environment settings
656 if self
.settings
.get("enable_icecream", False):
657 # Set the toolchain path
658 if self
.settings
.get("icecream_toolchain", None):
659 env
["ICECC_VERSION"] = self
.settings
.get("icecream_toolchain")
661 # Set preferred host if configured.
662 if self
.settings
.get("icecream_preferred_host", None):
663 env
["ICECC_PREFERRED_HOST"] = \
664 self
.settings
.get("icecream_preferred_host")
666 # Fake UTS_MACHINE, when we cannot use the personality syscall and
667 # if the host architecture is not equal to the target architecture.
668 if not self
.pakfire
.distro
.personality
and \
669 not system
.native_arch
== self
.pakfire
.distro
.arch
:
671 "LD_PRELOAD" : "/usr/lib/libpakfire_preload.so",
672 "UTS_MACHINE" : self
.pakfire
.distro
.arch
,
678 def installed_packages(self
):
680 Returns an iterator over all installed packages in this build environment.
682 # Get the repository of all installed packages.
683 repo
= self
.pakfire
.repos
.get_repo("@system")
685 # Return an iterator over the packages.
688 def write_config(self
):
689 # Cleanup everything in /etc/pakfire.
690 util
.rm(self
.chrootPath(CONFIG_DIR
))
692 for i
in (CONFIG_DIR
, CONFIG_REPOS_DIR
):
693 i
= self
.chrootPath(i
)
694 if not os
.path
.exists(i
):
697 # Write general.conf.
698 f
= open(self
.chrootPath(CONFIG_DIR
, "general.conf"), "w")
701 # Write builder.conf.
702 f
= open(self
.chrootPath(CONFIG_DIR
, "builder.conf"), "w")
703 f
.write(self
.distro
.get_config())
706 # Create pakfire configuration files.
707 for repo
in self
.pakfire
.repos
:
708 conf
= repo
.get_config()
713 filename
= self
.chrootPath(CONFIG_REPOS_DIR
, "%s.repo" % repo
.name
)
714 f
= open(filename
, "w")
715 f
.write("\n".join(conf
))
719 def pkg_makefile(self
):
720 return os
.path
.join(self
.build_dir
, "%s.%s" % (self
.pkg
.name
, MAKEFILE_EXTENSION
))
722 def execute(self
, command
, logger
=None, **kwargs
):
724 Executes the given command in the build chroot.
726 # Environment variables
729 if kwargs
.has_key("env"):
730 env
.update(kwargs
.pop("env"))
732 self
.log
.debug("Environment:")
733 for k
, v
in sorted(env
.items()):
734 self
.log
.debug(" %s=%s" % (k
, v
))
736 # Make every shell to a login shell because we set a lot of
737 # environment things there.
738 command
= ["bash", "--login", "-c", command
]
741 "chroot_path" : self
.chrootPath(),
742 "cgroup" : self
.cgroup
,
745 "personality" : self
.personality
,
751 shellenv
= shell
.ShellExecuteEnvironment(command
, **args
)
756 def execute_root(self
, command
, **kwargs
):
758 Executes the given command outside the build chroot.
760 shellenv
= shell
.ShellExecuteEnvironment(command
, **kwargs
)
765 def build(self
, install_test
=True, prepare
=False):
767 raise BuildError
, _("You cannot run a build when no package was given.")
769 # Search for the package file in build_dir and raise BuildError if it is not present.
770 if not os
.path
.exists(self
.pkg_makefile
):
771 raise BuildError
, _("Could not find makefile in build root: %s") % self
.pkg_makefile
773 # Write pakfire configuration into the chroot.
776 # Create the build command, that is executed in the chroot.
778 "/usr/lib/pakfire/builder",
781 "/%s" % os
.path
.relpath(self
.pkg_makefile
, self
.chrootPath()),
784 "--resultdir=/result",
787 # Check if only the preparation stage should be run.
789 build_command
.append("--prepare")
791 build_command
= " ".join(build_command
)
794 self
.execute(build_command
, logger
=self
.log
)
796 # Perform the install test after the actual build.
797 if install_test
and not prepare
:
800 except ShellEnvironmentError
:
801 self
.log
.error(_("Build failed"))
803 except KeyboardInterrupt:
804 self
.log
.error(_("Build interrupted"))
808 # Catch all other errors.
810 self
.log
.error(_("Build failed."), exc_info
=True)
813 # Don't sign packages in prepare mode.
817 # Sign all built packages with the host key (if available).
820 # Dump package information.
825 # End here in case of an error.
826 raise BuildError
, _("The build command failed. See logfile for details.")
828 def install_test(self
):
829 self
.log
.info(_("Running installation test..."))
831 # Install all packages that were built.
832 self
.install(self
.find_result_packages(), allow_vendorchange
=True,
833 allow_uninstall
=True, signatures_mode
="disabled")
835 self
.log
.info(_("Installation test succeeded."))
838 def shell(self
, args
=[]):
839 if not util
.cli_is_interactive():
840 self
.log
.warning("Cannot run shell on non-interactive console.")
843 # Install all packages that are needed to run a shell.
844 self
.install(SHELL_PACKAGES
)
846 # XXX need to set CFLAGS here
847 command
= "/usr/sbin/chroot %s %s %s" % \
848 (self
.chrootPath(), SHELL_SCRIPT
, " ".join(args
))
850 # Add personality if we require one
851 if self
.pakfire
.distro
.personality
:
852 command
= "%s %s" % (self
.pakfire
.distro
.personality
, command
)
854 for key
, val
in self
.environ
.items():
855 command
= "%s=\"%s\" " % (key
, val
) + command
857 # Empty the environment
858 command
= "env -i - %s" % command
860 self
.log
.debug("Shell command: %s" % command
)
862 shell
= os
.system(command
)
863 return os
.WEXITSTATUS(shell
)
865 def sign_packages(self
, keyfp
=None):
866 # Do nothing if signing is not requested.
867 if not self
.settings
.get("sign_packages"):
870 # Get key, that should be used for signing.
872 keyfp
= self
.keyring
.get_host_key_id()
874 # Find all files to process.
875 files
= self
.find_result_packages()
877 # Create a progressbar.
878 print _("Signing packages...")
879 p
= util
.make_progress(keyfp
, len(files
))
883 # Update progressbar.
889 pkg
= packages
.open(self
.pakfire
, None, file)
897 print "" # Print an empty line.
902 for file in self
.find_result_packages():
903 pkg
= packages
.open(self
.pakfire
, None, file)
906 # If there are no packages, there is nothing to do.
912 self
.log
.info(_("Dumping package information:"))
914 dump
= pkg
.dump(long=True)
916 for line
in dump
.splitlines():
917 self
.log
.info(" %s" % line
)
918 self
.log
.info("") # Empty line.
921 class Builder(object):
922 def __init__(self
, pakfire
, filename
, resultdir
, **kwargs
):
923 self
.pakfire
= pakfire
925 self
.filename
= filename
927 self
.resultdir
= resultdir
930 self
.pkg
= packages
.Makefile(self
.pakfire
, self
.filename
)
938 return self
.pkg
.buildroot
942 return self
.pakfire
.distro
948 # Get all definitions from the package.
949 environ
.update(self
.pkg
.exports
)
951 # Overwrite some definitions by default values.
952 environ
.update(self
._environ
)
956 def execute(self
, command
, logger
=None, **kwargs
):
958 logger
= logging
.getLogger("pakfire")
960 # Make every shell to a login shell because we set a lot of
961 # environment things there.
962 command
= ["bash", "--login", "-c", command
]
965 "cwd" : "/%s" % LOCAL_TMP_PATH
,
966 "env" : self
.environ
,
968 "personality" : self
.distro
.personality
,
974 shellenv
= shell
.ShellExecuteEnvironment(command
, **args
)
977 except ShellEnvironmentError
:
978 logger
.error("Command exited with an error: %s" % command
)
983 def run_script(self
, script
, *args
):
984 if not script
.startswith("/"):
985 script
= os
.path
.join(SCRIPT_DIR
, script
)
987 assert os
.path
.exists(script
), "Script we should run does not exist: %s" % script
994 # Returns the output of the command, but the output won't get
996 exe
= self
.execute(cmd
, record_output
=True, log_output
=False)
998 # Return the output of the command.
999 if exe
.exitcode
== 0:
1002 def create_icecream_toolchain(self
):
1005 "icecc --build-native 2>/dev/null",
1006 record_output
=True, record_stderr
=False,
1007 log_output
=False, log_errors
=False,
1010 except ShellEnvironmentError
:
1013 for line
in exe
.output
.splitlines():
1014 m
= re
.match(r
"^creating ([a-z0-9]+\.tar\.gz)", line
)
1016 self
._environ
["ICECC_VERSION"] = "/tmp/%s" % m
.group(1)
1018 def create_buildscript(self
, stage
):
1019 # Get buildscript from the package.
1020 script
= self
.pkg
.get_buildscript(stage
)
1022 # Write script to an empty file.
1023 f
= tempfile
.NamedTemporaryFile(mode
="w", delete
=False)
1024 f
.write("#!/bin/sh\n\n")
1027 f
.write("\n%s\n" % script
)
1031 # Make the script executable.
1032 os
.chmod(f
.name
, 700)
1036 def build(self
, stages
=None):
1037 # Create buildroot and remove all content if it was existant.
1038 util
.rm(self
.buildroot
)
1039 os
.makedirs(self
.buildroot
)
1041 # Build icecream toolchain if icecream is installed.
1042 self
.create_icecream_toolchain()
1044 # Process stages in order.
1045 for stage
in ("prepare", "build", "test", "install"):
1046 # Skip unwanted stages.
1047 if stages
and not stage
in stages
:
1051 self
.build_stage(stage
)
1053 # Stop if install stage has not been processed.
1054 if stages
and not "install" in stages
:
1057 # Run post-build stuff.
1058 self
.post_compress_man_pages()
1059 self
.post_remove_static_libs()
1060 self
.post_extract_debuginfo()
1062 # Package the result.
1063 # Make all these little package from the build environment.
1064 log
.info(_("Creating packages:"))
1066 for pkg
in reversed(self
.pkg
.packages
):
1067 packager
= packages
.packager
.BinaryPackager(self
.pakfire
, pkg
,
1068 self
, self
.buildroot
)
1069 pkg
= packager
.run(self
.resultdir
)
1073 def build_stage(self
, stage
):
1074 # Get the buildscript for this stage.
1075 buildscript
= self
.create_buildscript(stage
)
1077 # Execute the buildscript of this stage.
1078 log
.info(_("Running stage %s:") % stage
)
1081 self
.execute(buildscript
)
1084 # Remove the buildscript.
1085 if os
.path
.exists(buildscript
):
1086 os
.unlink(buildscript
)
1088 def post_remove_static_libs(self
):
1089 keep_libs
= self
.pkg
.lexer
.build
.get_var("keep_libraries")
1090 keep_libs
= keep_libs
.split()
1093 self
.execute("%s/remove-static-libs %s %s" % \
1094 (SCRIPT_DIR
, self
.buildroot
, " ".join(keep_libs
)))
1095 except ShellEnvironmentError
, e
:
1096 log
.warning(_("Could not remove static libraries: %s") % e
)
1098 def post_compress_man_pages(self
):
1100 self
.execute("%s/compress-man-pages %s" % (SCRIPT_DIR
, self
.buildroot
))
1101 except ShellEnvironmentError
, e
:
1102 log
.warning(_("Compressing man pages did not complete successfully."))
1104 def post_extract_debuginfo(self
):
1107 # Check if we need to run with strict build-id.
1108 strict_id
= self
.pkg
.lexer
.build
.get_var("debuginfo_strict_build_id", "true")
1109 if strict_id
in ("true", "yes", "1"):
1110 args
.append("--strict-build-id")
1112 args
.append("--buildroot=%s" % self
.pkg
.buildroot
)
1113 args
.append("--sourcedir=%s" % self
.pkg
.sourcedir
)
1115 # Get additional options to pass to script.
1116 options
= self
.pkg
.lexer
.build
.get_var("debuginfo_options", "")
1117 args
+= options
.split()
1120 self
.execute("%s/extract-debuginfo %s %s" % (SCRIPT_DIR
, " ".join(args
), self
.pkg
.buildroot
))
1121 except ShellEnvironmentError
, e
:
1122 log
.error(_("Extracting debuginfo did not complete with success. Aborting build."))
1125 def find_prerequires(self
, scriptlet_file
):
1126 assert os
.path
.exists(scriptlet_file
), "Scriptlet file does not exist: %s" % scriptlet_file
1128 res
= self
.run_script("find-prerequires", scriptlet_file
)
1129 prerequires
= set(res
.splitlines())
1134 if os
.path
.exists(self
.buildroot
):
1135 util
.rm(self
.buildroot
)