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 self
.cgroup
= cgroup
.CGroup("pakfire/builder/%s" % self
.build_id
)
314 # Attach the pakfire-builder process to the parent group.
315 self
.cgroup
.parent
.attach()
317 def init_logging(self
, logfile
):
319 self
.log
= log
.getChild(self
.build_id
)
320 # Propage everything to the root logger that we will see something
322 self
.log
.propagate
= 1
323 self
.log
.setLevel(logging
.INFO
)
325 # Add the given logfile to the logger.
326 h
= logging
.FileHandler(logfile
)
327 self
.log
.addHandler(h
)
329 # Format the log output for the file.
330 f
= logger
.BuildFormatter()
333 # If no logile was given, we use the root logger.
334 self
.log
= logging
.getLogger("pakfire")
336 def copyin(self
, file_out
, file_in
):
337 if file_in
.startswith("/"):
338 file_in
= file_in
[1:]
340 file_in
= self
.chrootPath(file_in
)
342 #if not os.path.exists(file_out):
345 dir_in
= os
.path
.dirname(file_in
)
346 if not os
.path
.exists(dir_in
):
349 self
.log
.debug("%s --> %s" % (file_out
, file_in
))
351 shutil
.copy2(file_out
, file_in
)
353 def copyout(self
, file_in
, file_out
):
354 if file_in
.startswith("/"):
355 file_in
= file_in
[1:]
357 file_in
= self
.chrootPath(file_in
)
359 #if not os.path.exists(file_in):
362 dir_out
= os
.path
.dirname(file_out
)
363 if not os
.path
.exists(dir_out
):
366 self
.log
.debug("%s --> %s" % (file_in
, file_out
))
368 shutil
.copy2(file_in
, file_out
)
370 def copy_result(self
, resultdir
):
371 # XXX should use find_result_packages
373 dir_in
= self
.chrootPath("result")
375 for dir, subdirs
, files
in os
.walk(dir_in
):
376 basename
= os
.path
.basename(dir)
377 dir = dir[len(self
.chrootPath()):]
379 file_in
= os
.path
.join(dir, file)
381 file_out
= os
.path
.join(
387 self
.copyout(file_in
, file_out
)
389 def find_result_packages(self
):
392 for dir, subdirs
, files
in os
.walk(self
.resultdir
):
394 if not file.endswith(".%s" % PACKAGE_EXTENSION
):
397 file = os
.path
.join(dir, file)
402 def extract(self
, requires
=None):
404 Gets a dependency set and extracts all packages
410 # Add neccessary build dependencies.
411 requires
+= BUILD_PACKAGES
413 # If we have ccache enabled, we need to extract it
414 # to the build chroot.
415 if self
.settings
.get("enable_ccache"):
416 requires
.append("ccache")
418 # If we have icecream enabled, we need to extract it
419 # to the build chroot.
420 if self
.settings
.get("enable_icecream"):
421 requires
.append("icecream")
423 # Get build dependencies from source package.
425 for req
in self
.pkg
.requires
:
428 # Install all packages.
429 self
.log
.info(_("Install packages needed for build..."))
430 self
.install(requires
)
432 # Copy the makefile and load source tarballs.
434 self
.pkg
.extract(_("Extracting"), prefix
=self
.build_dir
)
436 # Add an empty line at the end.
439 def install(self
, requires
, **kwargs
):
441 Install everything that is required in requires.
443 # If we got nothing to do, we quit immediately.
448 "interactive" : False,
452 if not kwargs
.has_key("allow_downgrade"):
453 kwargs
["allow_downgrade"] = True
455 # Install everything.
456 self
.pakfire
.install(requires
, **kwargs
)
458 def chrootPath(self
, *args
):
459 # Remove all leading slashes
462 if arg
.startswith("/"):
467 ret
= os
.path
.join(self
.path
, *args
)
468 ret
= ret
.replace("//", "/")
470 assert ret
.startswith(self
.path
)
474 def populate_dev(self
):
488 # If we need loop devices (which are optional) we create them here.
489 if self
.settings
["enable_loop_devices"]:
490 for i
in range(0, 7):
491 nodes
.append("/dev/loop%d" % i
)
494 # Stat the original node of the host system and copy it to
496 node_stat
= os
.stat(node
)
498 self
._create
_node
(node
, node_stat
.st_mode
, node_stat
.st_rdev
)
500 os
.symlink("/proc/self/fd/0", self
.chrootPath("dev", "stdin"))
501 os
.symlink("/proc/self/fd/1", self
.chrootPath("dev", "stdout"))
502 os
.symlink("/proc/self/fd/2", self
.chrootPath("dev", "stderr"))
503 os
.symlink("/proc/self/fd", self
.chrootPath("dev", "fd"))
507 Add DNS resolution facility to chroot environment by copying
508 /etc/resolv.conf and /etc/hosts.
510 for i
in ("/etc/resolv.conf", "/etc/hosts"):
513 def _create_node(self
, filename
, mode
, device
):
514 self
.log
.debug("Create node: %s (%s)" % (filename
, mode
))
516 filename
= self
.chrootPath(filename
)
518 # Create parent directory if it is missing.
519 dirname
= os
.path
.dirname(filename
)
520 if not os
.path
.exists(dirname
):
523 os
.mknod(filename
, mode
, device
)
526 self
.log
.debug("Destroying environment %s" % self
.path
)
528 if os
.path
.exists(self
.path
):
532 self
.log
.debug("Cleaning environemnt.")
534 # Remove the build directory and buildroot.
535 dirs
= (self
.build_dir
, self
.chrootPath("result"),)
538 if not os
.path
.exists(d
):
545 self
.log
.debug("Mounting environment")
546 for src
, dest
, fs
, options
in self
.mountpoints
:
547 mountpoint
= self
.chrootPath(dest
)
549 options
= "-o %s" % options
551 # Eventually create mountpoint directory
552 if not os
.path
.exists(mountpoint
):
553 os
.makedirs(mountpoint
)
555 self
.execute_root("mount -n -t %s %s %s %s" % (fs
, options
, src
, mountpoint
), shell
=True)
557 def _umountall(self
):
558 self
.log
.debug("Umounting environment")
561 for src
, dest
, fs
, options
in reversed(self
.mountpoints
):
562 dest
= self
.chrootPath(dest
)
564 if not dest
in mountpoints
:
565 mountpoints
.append(dest
)
568 for mp
in mountpoints
:
570 self
.execute_root("umount -n %s" % mp
, shell
=True)
571 except ShellEnvironmentError
:
574 if not os
.path
.ismount(mp
):
575 mountpoints
.remove(mp
)
578 def mountpoints(self
):
581 # Make root as a tmpfs if enabled.
582 if self
.settings
.get("buildroot_tmpfs"):
584 ("pakfire_root", "/", "tmpfs", "defaults"),
588 # src, dest, fs, options
589 ("pakfire_proc", "/proc", "proc", "nosuid,noexec,nodev"),
590 ("/proc/sys", "/proc/sys", "bind", "bind"),
591 ("/proc/sys", "/proc/sys", "bind", "bind,ro,remount"),
592 ("/sys", "/sys", "bind", "bind"),
593 ("/sys", "/sys", "bind", "bind,ro,remount"),
594 ("pakfire_tmpfs", "/dev", "tmpfs", "mode=755,nosuid"),
595 ("/dev/pts", "/dev/pts", "bind", "bind"),
596 ("pakfire_tmpfs", "/run", "tmpfs", "mode=755,nosuid,nodev"),
597 ("pakfire_tmpfs", "/tmp", "tmpfs", "mode=755,nosuid,nodev"),
600 # If selinux is enabled.
601 if os
.path
.exists("/sys/fs/selinux"):
603 ("/sys/fs/selinux", "/sys/fs/selinux", "bind", "bind"),
604 ("/sys/fs/selinux", "/sys/fs/selinux", "bind", "bind,ro,remount"),
607 # If ccache support is requested, we bind mount the cache.
608 if self
.settings
.get("enable_ccache"):
609 # Create ccache cache directory if it does not exist.
610 if not os
.path
.exists(CCACHE_CACHE_DIR
):
611 os
.makedirs(CCACHE_CACHE_DIR
)
614 (CCACHE_CACHE_DIR
, "/var/cache/ccache", "bind", "bind"),
622 # Add HOME manually, because it is occasionally not set
623 # and some builds get in trouble then.
624 "PATH" : "/usr/bin:/bin:/usr/sbin:/sbin",
626 "TERM" : os
.environ
.get("TERM", "vt100"),
630 "LANG" : os
.environ
.setdefault("LANG", "en_US.UTF-8"),
632 # Set the container that we can detect, if we are inside a
634 "container" : "pakfire-builder",
637 # Inherit environment from distro
638 env
.update(self
.pakfire
.distro
.environ
)
640 # ccache environment settings
641 if self
.settings
.get("enable_ccache", False):
642 compress
= self
.settings
.get("ccache_compress", False)
644 env
["CCACHE_COMPRESS"] = "1"
646 # Let ccache create its temporary files in /tmp.
647 env
["CCACHE_TEMPDIR"] = "/tmp"
649 # Icecream environment settings
650 if self
.settings
.get("enable_icecream", False):
651 # Set the toolchain path
652 if self
.settings
.get("icecream_toolchain", None):
653 env
["ICECC_VERSION"] = self
.settings
.get("icecream_toolchain")
655 # Set preferred host if configured.
656 if self
.settings
.get("icecream_preferred_host", None):
657 env
["ICECC_PREFERRED_HOST"] = \
658 self
.settings
.get("icecream_preferred_host")
660 # Fake UTS_MACHINE, when we cannot use the personality syscall and
661 # if the host architecture is not equal to the target architecture.
662 if not self
.pakfire
.distro
.personality
and \
663 not system
.native_arch
== self
.pakfire
.distro
.arch
:
665 "LD_PRELOAD" : "/usr/lib/libpakfire_preload.so",
666 "UTS_MACHINE" : self
.pakfire
.distro
.arch
,
672 def installed_packages(self
):
674 Returns an iterator over all installed packages in this build environment.
676 # Get the repository of all installed packages.
677 repo
= self
.pakfire
.repos
.get_repo("@system")
679 # Return an iterator over the packages.
682 def write_config(self
):
683 # Cleanup everything in /etc/pakfire.
684 util
.rm(self
.chrootPath(CONFIG_DIR
))
686 for i
in (CONFIG_DIR
, CONFIG_REPOS_DIR
):
687 i
= self
.chrootPath(i
)
688 if not os
.path
.exists(i
):
691 # Write general.conf.
692 f
= open(self
.chrootPath(CONFIG_DIR
, "general.conf"), "w")
695 # Write builder.conf.
696 f
= open(self
.chrootPath(CONFIG_DIR
, "builder.conf"), "w")
697 f
.write(self
.distro
.get_config())
700 # Create pakfire configuration files.
701 for repo
in self
.pakfire
.repos
:
702 conf
= repo
.get_config()
707 filename
= self
.chrootPath(CONFIG_REPOS_DIR
, "%s.repo" % repo
.name
)
708 f
= open(filename
, "w")
709 f
.write("\n".join(conf
))
713 def pkg_makefile(self
):
714 return os
.path
.join(self
.build_dir
, "%s.%s" % (self
.pkg
.name
, MAKEFILE_EXTENSION
))
716 def execute(self
, command
, logger
=None, **kwargs
):
718 Executes the given command in the build chroot.
720 # Environment variables
723 if kwargs
.has_key("env"):
724 env
.update(kwargs
.pop("env"))
726 self
.log
.debug("Environment:")
727 for k
, v
in sorted(env
.items()):
728 self
.log
.debug(" %s=%s" % (k
, v
))
730 # Make every shell to a login shell because we set a lot of
731 # environment things there.
732 command
= ["bash", "--login", "-c", command
]
735 "chroot_path" : self
.chrootPath(),
736 "cgroup" : self
.cgroup
,
739 "personality" : self
.personality
,
745 shellenv
= shell
.ShellExecuteEnvironment(command
, **args
)
750 def execute_root(self
, command
, **kwargs
):
752 Executes the given command outside the build chroot.
754 shellenv
= shell
.ShellExecuteEnvironment(command
, **kwargs
)
759 def build(self
, install_test
=True, prepare
=False):
761 raise BuildError
, _("You cannot run a build when no package was given.")
763 # Search for the package file in build_dir and raise BuildError if it is not present.
764 if not os
.path
.exists(self
.pkg_makefile
):
765 raise BuildError
, _("Could not find makefile in build root: %s") % self
.pkg_makefile
767 # Write pakfire configuration into the chroot.
770 # Create the build command, that is executed in the chroot.
772 "/usr/lib/pakfire/builder",
775 "/%s" % os
.path
.relpath(self
.pkg_makefile
, self
.chrootPath()),
778 "--resultdir=/result",
781 # Check if only the preparation stage should be run.
783 build_command
.append("--prepare")
785 build_command
= " ".join(build_command
)
788 self
.execute(build_command
, logger
=self
.log
)
790 # Perform the install test after the actual build.
791 if install_test
and not prepare
:
794 except ShellEnvironmentError
:
795 self
.log
.error(_("Build failed"))
797 except KeyboardInterrupt:
798 self
.log
.error(_("Build interrupted"))
802 # Catch all other errors.
804 self
.log
.error(_("Build failed."), exc_info
=True)
807 # Don't sign packages in prepare mode.
811 # Sign all built packages with the host key (if available).
814 # Dump package information.
819 # End here in case of an error.
820 raise BuildError
, _("The build command failed. See logfile for details.")
822 def install_test(self
):
823 self
.log
.info(_("Running installation test..."))
825 # Install all packages that were built.
826 self
.install(self
.find_result_packages(), allow_vendorchange
=True,
827 allow_uninstall
=True, signatures_mode
="disabled")
829 self
.log
.info(_("Installation test succeeded."))
832 def shell(self
, args
=[]):
833 if not util
.cli_is_interactive():
834 self
.log
.warning("Cannot run shell on non-interactive console.")
837 # Install all packages that are needed to run a shell.
838 self
.install(SHELL_PACKAGES
)
840 # XXX need to set CFLAGS here
841 command
= "/usr/sbin/chroot %s %s %s" % \
842 (self
.chrootPath(), SHELL_SCRIPT
, " ".join(args
))
844 # Add personality if we require one
845 if self
.pakfire
.distro
.personality
:
846 command
= "%s %s" % (self
.pakfire
.distro
.personality
, command
)
848 for key
, val
in self
.environ
.items():
849 command
= "%s=\"%s\" " % (key
, val
) + command
851 # Empty the environment
852 command
= "env -i - %s" % command
854 self
.log
.debug("Shell command: %s" % command
)
856 shell
= os
.system(command
)
857 return os
.WEXITSTATUS(shell
)
859 def sign_packages(self
, keyfp
=None):
860 # Do nothing if signing is not requested.
861 if not self
.settings
.get("sign_packages"):
864 # Get key, that should be used for signing.
866 keyfp
= self
.keyring
.get_host_key_id()
868 # Find all files to process.
869 files
= self
.find_result_packages()
871 # Create a progressbar.
872 print _("Signing packages...")
873 p
= util
.make_progress(keyfp
, len(files
))
877 # Update progressbar.
883 pkg
= packages
.open(self
.pakfire
, None, file)
891 print "" # Print an empty line.
896 for file in self
.find_result_packages():
897 pkg
= packages
.open(self
.pakfire
, None, file)
900 # If there are no packages, there is nothing to do.
906 self
.log
.info(_("Dumping package information:"))
908 dump
= pkg
.dump(long=True)
910 for line
in dump
.splitlines():
911 self
.log
.info(" %s" % line
)
912 self
.log
.info("") # Empty line.
915 class Builder(object):
916 def __init__(self
, pakfire
, filename
, resultdir
, **kwargs
):
917 self
.pakfire
= pakfire
919 self
.filename
= filename
921 self
.resultdir
= resultdir
924 self
.pkg
= packages
.Makefile(self
.pakfire
, self
.filename
)
932 return self
.pkg
.buildroot
936 return self
.pakfire
.distro
942 # Get all definitions from the package.
943 environ
.update(self
.pkg
.exports
)
945 # Overwrite some definitions by default values.
946 environ
.update(self
._environ
)
950 def execute(self
, command
, logger
=None, **kwargs
):
952 logger
= logging
.getLogger("pakfire")
954 # Make every shell to a login shell because we set a lot of
955 # environment things there.
956 command
= ["bash", "--login", "-c", command
]
959 "cwd" : "/%s" % LOCAL_TMP_PATH
,
960 "env" : self
.environ
,
962 "personality" : self
.distro
.personality
,
968 shellenv
= shell
.ShellExecuteEnvironment(command
, **args
)
971 except ShellEnvironmentError
:
972 logger
.error("Command exited with an error: %s" % command
)
977 def run_script(self
, script
, *args
):
978 if not script
.startswith("/"):
979 script
= os
.path
.join(SCRIPT_DIR
, script
)
981 assert os
.path
.exists(script
), "Script we should run does not exist: %s" % script
988 # Returns the output of the command, but the output won't get
990 exe
= self
.execute(cmd
, record_output
=True, log_output
=False)
992 # Return the output of the command.
993 if exe
.exitcode
== 0:
996 def create_icecream_toolchain(self
):
999 "icecc --build-native 2>/dev/null",
1000 record_output
=True, record_stderr
=False,
1001 log_output
=False, log_errors
=False,
1004 except ShellEnvironmentError
:
1007 for line
in exe
.output
.splitlines():
1008 m
= re
.match(r
"^creating ([a-z0-9]+\.tar\.gz)", line
)
1010 self
._environ
["ICECC_VERSION"] = "/tmp/%s" % m
.group(1)
1012 def create_buildscript(self
, stage
):
1013 # Get buildscript from the package.
1014 script
= self
.pkg
.get_buildscript(stage
)
1016 # Write script to an empty file.
1017 f
= tempfile
.NamedTemporaryFile(mode
="w", delete
=False)
1018 f
.write("#!/bin/sh\n\n")
1021 f
.write("\n%s\n" % script
)
1025 # Make the script executable.
1026 os
.chmod(f
.name
, 700)
1030 def build(self
, stages
=None):
1031 # Create buildroot and remove all content if it was existant.
1032 util
.rm(self
.buildroot
)
1033 os
.makedirs(self
.buildroot
)
1035 # Build icecream toolchain if icecream is installed.
1036 self
.create_icecream_toolchain()
1038 # Process stages in order.
1039 for stage
in ("prepare", "build", "test", "install"):
1040 # Skip unwanted stages.
1041 if stages
and not stage
in stages
:
1045 self
.build_stage(stage
)
1047 # Stop if install stage has not been processed.
1048 if stages
and not "install" in stages
:
1051 # Run post-build stuff.
1052 self
.post_compress_man_pages()
1053 self
.post_remove_static_libs()
1054 self
.post_extract_debuginfo()
1056 # Package the result.
1057 # Make all these little package from the build environment.
1058 log
.info(_("Creating packages:"))
1060 for pkg
in reversed(self
.pkg
.packages
):
1061 packager
= packages
.packager
.BinaryPackager(self
.pakfire
, pkg
,
1062 self
, self
.buildroot
)
1063 pkg
= packager
.run(self
.resultdir
)
1067 def build_stage(self
, stage
):
1068 # Get the buildscript for this stage.
1069 buildscript
= self
.create_buildscript(stage
)
1071 # Execute the buildscript of this stage.
1072 log
.info(_("Running stage %s:") % stage
)
1075 self
.execute(buildscript
)
1078 # Remove the buildscript.
1079 if os
.path
.exists(buildscript
):
1080 os
.unlink(buildscript
)
1082 def post_remove_static_libs(self
):
1083 keep_libs
= self
.pkg
.lexer
.build
.get_var("keep_libraries")
1084 keep_libs
= keep_libs
.split()
1087 self
.execute("%s/remove-static-libs %s %s" % \
1088 (SCRIPT_DIR
, self
.buildroot
, " ".join(keep_libs
)))
1089 except ShellEnvironmentError
, e
:
1090 log
.warning(_("Could not remove static libraries: %s") % e
)
1092 def post_compress_man_pages(self
):
1094 self
.execute("%s/compress-man-pages %s" % (SCRIPT_DIR
, self
.buildroot
))
1095 except ShellEnvironmentError
, e
:
1096 log
.warning(_("Compressing man pages did not complete successfully."))
1098 def post_extract_debuginfo(self
):
1101 # Check if we need to run with strict build-id.
1102 strict_id
= self
.pkg
.lexer
.build
.get_var("debuginfo_strict_build_id", "true")
1103 if strict_id
in ("true", "yes", "1"):
1104 args
.append("--strict-build-id")
1106 args
.append("--buildroot=%s" % self
.pkg
.buildroot
)
1107 args
.append("--sourcedir=%s" % self
.pkg
.sourcedir
)
1109 # Get additional options to pass to script.
1110 options
= self
.pkg
.lexer
.build
.get_var("debuginfo_options", "")
1111 args
+= options
.split()
1114 self
.execute("%s/extract-debuginfo %s %s" % (SCRIPT_DIR
, " ".join(args
), self
.pkg
.buildroot
))
1115 except ShellEnvironmentError
, e
:
1116 log
.error(_("Extracting debuginfo did not complete with success. Aborting build."))
1119 def find_prerequires(self
, scriptlet_file
):
1120 assert os
.path
.exists(scriptlet_file
), "Scriptlet file does not exist: %s" % scriptlet_file
1122 res
= self
.run_script("find-prerequires", scriptlet_file
)
1123 prerequires
= set(res
.splitlines())
1128 if os
.path
.exists(self
.buildroot
):
1129 util
.rm(self
.buildroot
)