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.
183 if e
.errno
== 30: # Read-only FS
184 raise BuildError
, "Buildroot is read-only: %s" % self
.pakfire
.path
186 # Raise all other errors.
189 # Lock the build environment.
192 # Initialize pakfire instance.
193 self
.pakfire
.initialize()
195 # Optionally enable private networking.
196 if self
.settings
.get("private_network", None):
197 _pakfire
.unshare(_pakfire
.SCHED_CLONE_NEWNET
)
202 # Setup domain name resolution in chroot.
205 # Extract all needed packages.
210 # Move the builder process out of the cgroup.
211 self
.cgroup
.migrate_task(self
.cgroup
.parent
, os
.getpid())
213 # Kill all still running processes in the cgroup.
214 self
.cgroup
.kill_and_wait()
216 # Remove cgroup and all parent cgroups if they are empty.
217 self
.cgroup
.destroy()
219 parent
= self
.cgroup
.parent
221 if not parent
.is_empty(recursive
=True):
225 parent
= parent
.parent
228 util
.orphans_kill(self
.path
)
230 # Shut down pakfire instance.
231 self
.pakfire
.destroy()
233 # Unlock build environment.
236 # Umount the build environment.
245 Proxy method for easy access to the configuration.
247 return self
.pakfire
.config
252 Proxy method for easy access to the distribution.
254 return self
.pakfire
.distro
259 Proxy method for easy access to the path.
261 return self
.pakfire
.path
266 Inherit architecture from distribution configuration.
268 return self
.pakfire
.distro
.arch
271 def personality(self
):
273 Gets the personality from the distribution configuration.
275 return self
.pakfire
.distro
.personality
280 "build_date" : time
.strftime("%a, %d %b %Y %H:%M:%S +0000", time
.gmtime(self
.build_time
)),
281 "build_host" : socket
.gethostname(),
282 "build_id" : self
.build_id
,
283 "build_time" : self
.build_time
,
289 Shortcut to access the pakfire keyring.
291 return self
.pakfire
.keyring
294 filename
= os
.path
.join(self
.path
, ".lock")
297 self
._lock
= open(filename
, "a+")
302 fcntl
.lockf(self
._lock
.fileno(), fcntl
.LOCK_EX | fcntl
.LOCK_NB
)
304 raise BuildRootLocked
, "Buildroot is locked"
313 def init_cgroup(self
):
315 Initialize cgroup (if the system supports it).
317 if not cgroup
.supported():
321 # Search for the cgroup this process is currently running in.
322 parent_cgroup
= cgroup
.find_by_pid(os
.getpid())
323 if not parent_cgroup
:
326 # Create our own cgroup inside the parent cgroup.
327 self
.cgroup
= parent_cgroup
.create_child_cgroup("pakfire/builder/%s" % self
.build_id
)
329 # Attach the pakfire-builder process to the group.
332 def init_logging(self
, logfile
):
334 self
.log
= log
.getChild(self
.build_id
)
335 # Propage everything to the root logger that we will see something
337 self
.log
.propagate
= 1
338 self
.log
.setLevel(logging
.INFO
)
340 # Add the given logfile to the logger.
341 h
= logging
.FileHandler(logfile
)
342 self
.log
.addHandler(h
)
344 # Format the log output for the file.
345 f
= logger
.BuildFormatter()
348 # If no logile was given, we use the root logger.
349 self
.log
= logging
.getLogger("pakfire")
351 def copyin(self
, file_out
, file_in
):
352 if file_in
.startswith("/"):
353 file_in
= file_in
[1:]
355 file_in
= self
.chrootPath(file_in
)
357 #if not os.path.exists(file_out):
360 dir_in
= os
.path
.dirname(file_in
)
361 if not os
.path
.exists(dir_in
):
364 self
.log
.debug("%s --> %s" % (file_out
, file_in
))
366 shutil
.copy2(file_out
, file_in
)
368 def copyout(self
, file_in
, file_out
):
369 if file_in
.startswith("/"):
370 file_in
= file_in
[1:]
372 file_in
= self
.chrootPath(file_in
)
374 #if not os.path.exists(file_in):
377 dir_out
= os
.path
.dirname(file_out
)
378 if not os
.path
.exists(dir_out
):
381 self
.log
.debug("%s --> %s" % (file_in
, file_out
))
383 shutil
.copy2(file_in
, file_out
)
385 def copy_result(self
, resultdir
):
386 # XXX should use find_result_packages
388 dir_in
= self
.chrootPath("result")
390 for dir, subdirs
, files
in os
.walk(dir_in
):
391 basename
= os
.path
.basename(dir)
392 dir = dir[len(self
.chrootPath()):]
394 file_in
= os
.path
.join(dir, file)
396 file_out
= os
.path
.join(
402 self
.copyout(file_in
, file_out
)
404 def find_result_packages(self
):
407 for dir, subdirs
, files
in os
.walk(self
.resultdir
):
409 if not file.endswith(".%s" % PACKAGE_EXTENSION
):
412 file = os
.path
.join(dir, file)
417 def extract(self
, requires
=None):
419 Gets a dependency set and extracts all packages
425 # Add neccessary build dependencies.
426 requires
+= BUILD_PACKAGES
428 # If we have ccache enabled, we need to extract it
429 # to the build chroot.
430 if self
.settings
.get("enable_ccache"):
431 requires
.append("ccache")
433 # If we have icecream enabled, we need to extract it
434 # to the build chroot.
435 if self
.settings
.get("enable_icecream"):
436 requires
.append("icecream")
438 # Get build dependencies from source package.
440 for req
in self
.pkg
.requires
:
443 # Install all packages.
444 self
.log
.info(_("Install packages needed for build..."))
445 self
.install(requires
)
447 # Copy the makefile and load source tarballs.
449 self
.pkg
.extract(_("Extracting"), prefix
=self
.build_dir
)
451 # Add an empty line at the end.
454 def install(self
, requires
, **kwargs
):
456 Install everything that is required in requires.
458 # If we got nothing to do, we quit immediately.
463 "interactive" : False,
467 if not kwargs
.has_key("allow_downgrade"):
468 kwargs
["allow_downgrade"] = True
470 # Install everything.
471 self
.pakfire
.install(requires
, **kwargs
)
473 def chrootPath(self
, *args
):
474 # Remove all leading slashes
477 if arg
.startswith("/"):
482 ret
= os
.path
.join(self
.path
, *args
)
483 ret
= ret
.replace("//", "/")
485 assert ret
.startswith(self
.path
)
489 def populate_dev(self
):
503 # If we need loop devices (which are optional) we create them here.
504 if self
.settings
["enable_loop_devices"]:
505 for i
in range(0, 7):
506 nodes
.append("/dev/loop%d" % i
)
509 # Stat the original node of the host system and copy it to
512 node_stat
= os
.stat(node
)
514 # If it cannot be found, just go on.
518 self
._create
_node
(node
, node_stat
.st_mode
, node_stat
.st_rdev
)
520 os
.symlink("/proc/self/fd/0", self
.chrootPath("dev", "stdin"))
521 os
.symlink("/proc/self/fd/1", self
.chrootPath("dev", "stdout"))
522 os
.symlink("/proc/self/fd/2", self
.chrootPath("dev", "stderr"))
523 os
.symlink("/proc/self/fd", self
.chrootPath("dev", "fd"))
527 Add DNS resolution facility to chroot environment by copying
528 /etc/resolv.conf and /etc/hosts.
530 for i
in ("/etc/resolv.conf", "/etc/hosts"):
533 def _create_node(self
, filename
, mode
, device
):
534 self
.log
.debug("Create node: %s (%s)" % (filename
, mode
))
536 filename
= self
.chrootPath(filename
)
538 # Create parent directory if it is missing.
539 dirname
= os
.path
.dirname(filename
)
540 if not os
.path
.exists(dirname
):
543 os
.mknod(filename
, mode
, device
)
546 self
.log
.debug("Destroying environment %s" % self
.path
)
548 if os
.path
.exists(self
.path
):
552 self
.log
.debug("Cleaning environemnt.")
554 # Remove the build directory and buildroot.
555 dirs
= (self
.build_dir
, self
.chrootPath("result"),)
558 if not os
.path
.exists(d
):
565 self
.log
.debug("Mounting environment")
566 for src
, dest
, fs
, options
in self
.mountpoints
:
567 mountpoint
= self
.chrootPath(dest
)
569 options
= "-o %s" % options
571 # Eventually create mountpoint directory
572 if not os
.path
.exists(mountpoint
):
573 os
.makedirs(mountpoint
)
575 self
.execute_root("mount -n -t %s %s %s %s" % (fs
, options
, src
, mountpoint
), shell
=True)
577 def _umountall(self
):
578 self
.log
.debug("Umounting environment")
581 for src
, dest
, fs
, options
in reversed(self
.mountpoints
):
582 dest
= self
.chrootPath(dest
)
584 if not dest
in mountpoints
:
585 mountpoints
.append(dest
)
588 for mp
in mountpoints
:
590 self
.execute_root("umount -n %s" % mp
, shell
=True)
591 except ShellEnvironmentError
:
594 if not os
.path
.ismount(mp
):
595 mountpoints
.remove(mp
)
598 def mountpoints(self
):
601 # Make root as a tmpfs if enabled.
602 if self
.settings
.get("buildroot_tmpfs"):
604 ("pakfire_root", "/", "tmpfs", "defaults"),
608 # src, dest, fs, options
609 ("pakfire_proc", "/proc", "proc", "nosuid,noexec,nodev"),
610 ("/proc/sys", "/proc/sys", "bind", "bind"),
611 ("/proc/sys", "/proc/sys", "bind", "bind,ro,remount"),
612 ("/sys", "/sys", "bind", "bind"),
613 ("/sys", "/sys", "bind", "bind,ro,remount"),
614 ("pakfire_tmpfs", "/dev", "tmpfs", "mode=755,nosuid"),
615 ("/dev/pts", "/dev/pts", "bind", "bind"),
616 ("pakfire_tmpfs", "/run", "tmpfs", "mode=755,nosuid,nodev"),
617 ("pakfire_tmpfs", "/tmp", "tmpfs", "mode=755,nosuid,nodev"),
620 # If selinux is enabled.
621 if os
.path
.exists("/sys/fs/selinux"):
623 ("/sys/fs/selinux", "/sys/fs/selinux", "bind", "bind"),
624 ("/sys/fs/selinux", "/sys/fs/selinux", "bind", "bind,ro,remount"),
627 # If ccache support is requested, we bind mount the cache.
628 if self
.settings
.get("enable_ccache"):
629 # Create ccache cache directory if it does not exist.
630 if not os
.path
.exists(CCACHE_CACHE_DIR
):
631 os
.makedirs(CCACHE_CACHE_DIR
)
634 (CCACHE_CACHE_DIR
, "/var/cache/ccache", "bind", "bind"),
642 # Add HOME manually, because it is occasionally not set
643 # and some builds get in trouble then.
644 "PATH" : "/usr/bin:/bin:/usr/sbin:/sbin",
646 "TERM" : os
.environ
.get("TERM", "vt100"),
650 "LANG" : os
.environ
.setdefault("LANG", "en_US.UTF-8"),
652 # Set the container that we can detect, if we are inside a
654 "container" : "pakfire-builder",
657 # Inherit environment from distro
658 env
.update(self
.pakfire
.distro
.environ
)
660 # ccache environment settings
661 if self
.settings
.get("enable_ccache", False):
662 compress
= self
.settings
.get("ccache_compress", False)
664 env
["CCACHE_COMPRESS"] = "1"
666 # Let ccache create its temporary files in /tmp.
667 env
["CCACHE_TEMPDIR"] = "/tmp"
669 # Icecream environment settings
670 if self
.settings
.get("enable_icecream", False):
671 # Set the toolchain path
672 if self
.settings
.get("icecream_toolchain", None):
673 env
["ICECC_VERSION"] = self
.settings
.get("icecream_toolchain")
675 # Set preferred host if configured.
676 if self
.settings
.get("icecream_preferred_host", None):
677 env
["ICECC_PREFERRED_HOST"] = \
678 self
.settings
.get("icecream_preferred_host")
680 # Fake UTS_MACHINE, when we cannot use the personality syscall and
681 # if the host architecture is not equal to the target architecture.
682 if not self
.pakfire
.distro
.personality
and \
683 not system
.native_arch
== self
.pakfire
.distro
.arch
:
685 "LD_PRELOAD" : "/usr/lib/libpakfire_preload.so",
686 "UTS_MACHINE" : self
.pakfire
.distro
.arch
,
692 def installed_packages(self
):
694 Returns an iterator over all installed packages in this build environment.
696 # Get the repository of all installed packages.
697 repo
= self
.pakfire
.repos
.get_repo("@system")
699 # Return an iterator over the packages.
702 def write_config(self
):
703 # Cleanup everything in /etc/pakfire.
704 util
.rm(self
.chrootPath(CONFIG_DIR
))
706 for i
in (CONFIG_DIR
, CONFIG_REPOS_DIR
):
707 i
= self
.chrootPath(i
)
708 if not os
.path
.exists(i
):
711 # Write general.conf.
712 f
= open(self
.chrootPath(CONFIG_DIR
, "general.conf"), "w")
715 # Write builder.conf.
716 f
= open(self
.chrootPath(CONFIG_DIR
, "builder.conf"), "w")
717 f
.write(self
.distro
.get_config())
720 # Create pakfire configuration files.
721 for repo
in self
.pakfire
.repos
:
722 conf
= repo
.get_config()
727 filename
= self
.chrootPath(CONFIG_REPOS_DIR
, "%s.repo" % repo
.name
)
728 f
= open(filename
, "w")
729 f
.write("\n".join(conf
))
733 def pkg_makefile(self
):
734 return os
.path
.join(self
.build_dir
, "%s.%s" % (self
.pkg
.name
, MAKEFILE_EXTENSION
))
736 def execute(self
, command
, logger
=None, **kwargs
):
738 Executes the given command in the build chroot.
740 # Environment variables
743 if kwargs
.has_key("env"):
744 env
.update(kwargs
.pop("env"))
746 self
.log
.debug("Environment:")
747 for k
, v
in sorted(env
.items()):
748 self
.log
.debug(" %s=%s" % (k
, v
))
750 # Make every shell to a login shell because we set a lot of
751 # environment things there.
752 command
= ["bash", "--login", "-c", command
]
755 "chroot_path" : self
.chrootPath(),
756 "cgroup" : self
.cgroup
,
759 "personality" : self
.personality
,
765 shellenv
= shell
.ShellExecuteEnvironment(command
, **args
)
770 def execute_root(self
, command
, **kwargs
):
772 Executes the given command outside the build chroot.
774 shellenv
= shell
.ShellExecuteEnvironment(command
, **kwargs
)
779 def build(self
, install_test
=True, prepare
=False):
781 raise BuildError
, _("You cannot run a build when no package was given.")
783 # Search for the package file in build_dir and raise BuildError if it is not present.
784 if not os
.path
.exists(self
.pkg_makefile
):
785 raise BuildError
, _("Could not find makefile in build root: %s") % self
.pkg_makefile
787 # Write pakfire configuration into the chroot.
790 # Create the build command, that is executed in the chroot.
792 "/usr/lib/pakfire/builder",
795 "/%s" % os
.path
.relpath(self
.pkg_makefile
, self
.chrootPath()),
798 "--resultdir=/result",
801 # Check if only the preparation stage should be run.
803 build_command
.append("--prepare")
805 build_command
= " ".join(build_command
)
808 self
.execute(build_command
, logger
=self
.log
)
810 # Perform the install test after the actual build.
811 if install_test
and not prepare
:
814 except ShellEnvironmentError
:
815 self
.log
.error(_("Build failed"))
817 except KeyboardInterrupt:
818 self
.log
.error(_("Build interrupted"))
822 # Catch all other errors.
824 self
.log
.error(_("Build failed."), exc_info
=True)
827 # Don't sign packages in prepare mode.
831 # Sign all built packages with the host key (if available).
834 # Dump package information.
839 # End here in case of an error.
840 raise BuildError
, _("The build command failed. See logfile for details.")
842 def install_test(self
):
843 self
.log
.info(_("Running installation test..."))
845 # Install all packages that were built.
846 self
.install(self
.find_result_packages(), allow_vendorchange
=True,
847 allow_uninstall
=True, signatures_mode
="disabled")
849 self
.log
.info(_("Installation test succeeded."))
852 def shell(self
, args
=[]):
853 if not util
.cli_is_interactive():
854 self
.log
.warning("Cannot run shell on non-interactive console.")
857 # Install all packages that are needed to run a shell.
858 self
.install(SHELL_PACKAGES
)
860 # XXX need to set CFLAGS here
861 command
= "/usr/sbin/chroot %s %s %s" % \
862 (self
.chrootPath(), SHELL_SCRIPT
, " ".join(args
))
864 # Add personality if we require one
865 if self
.pakfire
.distro
.personality
:
866 command
= "%s %s" % (self
.pakfire
.distro
.personality
, command
)
868 for key
, val
in self
.environ
.items():
869 command
= "%s=\"%s\" " % (key
, val
) + command
871 # Empty the environment
872 command
= "env -i - %s" % command
874 self
.log
.debug("Shell command: %s" % command
)
876 shell
= os
.system(command
)
877 return os
.WEXITSTATUS(shell
)
879 def sign_packages(self
, keyfp
=None):
880 # Do nothing if signing is not requested.
881 if not self
.settings
.get("sign_packages"):
884 # Get key, that should be used for signing.
886 keyfp
= self
.keyring
.get_host_key_id()
888 # Find all files to process.
889 files
= self
.find_result_packages()
891 # Create a progressbar.
892 print _("Signing packages...")
893 p
= util
.make_progress(keyfp
, len(files
))
897 # Update progressbar.
903 pkg
= packages
.open(self
.pakfire
, None, file)
911 print "" # Print an empty line.
916 for file in self
.find_result_packages():
917 pkg
= packages
.open(self
.pakfire
, None, file)
920 # If there are no packages, there is nothing to do.
926 self
.log
.info(_("Dumping package information:"))
928 dump
= pkg
.dump(long=True)
930 for line
in dump
.splitlines():
931 self
.log
.info(" %s" % line
)
932 self
.log
.info("") # Empty line.
935 class Builder(object):
936 def __init__(self
, pakfire
, filename
, resultdir
, **kwargs
):
937 self
.pakfire
= pakfire
939 self
.filename
= filename
941 self
.resultdir
= resultdir
944 self
.pkg
= packages
.Makefile(self
.pakfire
, self
.filename
)
952 return self
.pkg
.buildroot
956 return self
.pakfire
.distro
962 # Get all definitions from the package.
963 environ
.update(self
.pkg
.exports
)
965 # Overwrite some definitions by default values.
966 environ
.update(self
._environ
)
970 def execute(self
, command
, logger
=None, **kwargs
):
972 logger
= logging
.getLogger("pakfire")
974 # Make every shell to a login shell because we set a lot of
975 # environment things there.
976 command
= ["bash", "--login", "-c", command
]
979 "cwd" : "/%s" % LOCAL_TMP_PATH
,
980 "env" : self
.environ
,
982 "personality" : self
.distro
.personality
,
988 shellenv
= shell
.ShellExecuteEnvironment(command
, **args
)
991 except ShellEnvironmentError
:
992 logger
.error("Command exited with an error: %s" % command
)
997 def run_script(self
, script
, *args
):
998 if not script
.startswith("/"):
999 script
= os
.path
.join(SCRIPT_DIR
, script
)
1001 assert os
.path
.exists(script
), "Script we should run does not exist: %s" % script
1008 # Returns the output of the command, but the output won't get
1010 exe
= self
.execute(cmd
, record_output
=True, log_output
=False)
1012 # Return the output of the command.
1013 if exe
.exitcode
== 0:
1016 def create_icecream_toolchain(self
):
1019 "icecc --build-native 2>/dev/null",
1020 record_output
=True, record_stderr
=False,
1021 log_output
=False, log_errors
=False,
1024 except ShellEnvironmentError
:
1027 for line
in exe
.output
.splitlines():
1028 m
= re
.match(r
"^creating ([a-z0-9]+\.tar\.gz)", line
)
1030 self
._environ
["ICECC_VERSION"] = "/tmp/%s" % m
.group(1)
1032 def create_buildscript(self
, stage
):
1033 # Get buildscript from the package.
1034 script
= self
.pkg
.get_buildscript(stage
)
1036 # Write script to an empty file.
1037 f
= tempfile
.NamedTemporaryFile(mode
="w", delete
=False)
1038 f
.write("#!/bin/sh\n\n")
1041 f
.write("\n%s\n" % script
)
1045 # Make the script executable.
1046 os
.chmod(f
.name
, 700)
1050 def build(self
, stages
=None):
1051 # Create buildroot and remove all content if it was existant.
1052 util
.rm(self
.buildroot
)
1053 os
.makedirs(self
.buildroot
)
1055 # Build icecream toolchain if icecream is installed.
1056 self
.create_icecream_toolchain()
1058 # Process stages in order.
1059 for stage
in ("prepare", "build", "test", "install"):
1060 # Skip unwanted stages.
1061 if stages
and not stage
in stages
:
1065 self
.build_stage(stage
)
1067 # Stop if install stage has not been processed.
1068 if stages
and not "install" in stages
:
1071 # Run post-build stuff.
1072 self
.post_compress_man_pages()
1073 self
.post_remove_static_libs()
1074 self
.post_extract_debuginfo()
1076 # Package the result.
1077 # Make all these little package from the build environment.
1078 log
.info(_("Creating packages:"))
1080 for pkg
in reversed(self
.pkg
.packages
):
1081 packager
= packages
.packager
.BinaryPackager(self
.pakfire
, pkg
,
1082 self
, self
.buildroot
)
1083 pkg
= packager
.run(self
.resultdir
)
1087 def build_stage(self
, stage
):
1088 # Get the buildscript for this stage.
1089 buildscript
= self
.create_buildscript(stage
)
1091 # Execute the buildscript of this stage.
1092 log
.info(_("Running stage %s:") % stage
)
1095 self
.execute(buildscript
)
1098 # Remove the buildscript.
1099 if os
.path
.exists(buildscript
):
1100 os
.unlink(buildscript
)
1102 def post_remove_static_libs(self
):
1103 keep_libs
= self
.pkg
.lexer
.build
.get_var("keep_libraries")
1104 keep_libs
= keep_libs
.split()
1107 self
.execute("%s/remove-static-libs %s %s" % \
1108 (SCRIPT_DIR
, self
.buildroot
, " ".join(keep_libs
)))
1109 except ShellEnvironmentError
, e
:
1110 log
.warning(_("Could not remove static libraries: %s") % e
)
1112 def post_compress_man_pages(self
):
1114 self
.execute("%s/compress-man-pages %s" % (SCRIPT_DIR
, self
.buildroot
))
1115 except ShellEnvironmentError
, e
:
1116 log
.warning(_("Compressing man pages did not complete successfully."))
1118 def post_extract_debuginfo(self
):
1121 # Check if we need to run with strict build-id.
1122 strict_id
= self
.pkg
.lexer
.build
.get_var("debuginfo_strict_build_id", "true")
1123 if strict_id
in ("true", "yes", "1"):
1124 args
.append("--strict-build-id")
1126 args
.append("--buildroot=%s" % self
.pkg
.buildroot
)
1127 args
.append("--sourcedir=%s" % self
.pkg
.sourcedir
)
1129 # Get additional options to pass to script.
1130 options
= self
.pkg
.lexer
.build
.get_var("debuginfo_options", "")
1131 args
+= options
.split()
1134 self
.execute("%s/extract-debuginfo %s %s" % (SCRIPT_DIR
, " ".join(args
), self
.pkg
.buildroot
))
1135 except ShellEnvironmentError
, e
:
1136 log
.error(_("Extracting debuginfo did not complete with success. Aborting build."))
1139 def find_prerequires(self
, scriptlet_file
):
1140 assert os
.path
.exists(scriptlet_file
), "Scriptlet file does not exist: %s" % scriptlet_file
1142 res
= self
.run_script("find-prerequires", scriptlet_file
)
1143 prerequires
= set(res
.splitlines())
1148 if os
.path
.exists(self
.buildroot
):
1149 util
.rm(self
.buildroot
)