693 lines
26 KiB
Python
693 lines
26 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""
|
|
|
|
@author: Fabio Erculiani <lxnay@sabayon.org>
|
|
@contact: lxnay@sabayon.org
|
|
@copyright: Fabio Erculiani
|
|
@license: GPL-2
|
|
|
|
B{Matter TinderBox Toolkit}.
|
|
|
|
"""
|
|
import os
|
|
import shlex
|
|
import shutil
|
|
import subprocess
|
|
import tempfile
|
|
|
|
from matter.utils import mkstemp, mkdtemp, print_traceback
|
|
from matter.output import is_stdout_a_tty, print_info, print_warning, \
|
|
print_error, getcolor, darkgreen, purple
|
|
|
|
from _emerge.depgraph import backtrack_depgraph
|
|
from _emerge.actions import load_emerge_config
|
|
try:
|
|
from _emerge.actions import validate_ebuild_environment
|
|
except ImportError:
|
|
# older portage versions
|
|
from _emerge.main import validate_ebuild_environment
|
|
try:
|
|
from _emerge.post_emerge import post_emerge
|
|
except ImportError:
|
|
# older portage versions
|
|
from _emerge.main import post_emerge
|
|
from _emerge.create_depgraph_params import create_depgraph_params
|
|
from _emerge.main import parse_opts
|
|
from _emerge.stdout_spinner import stdout_spinner
|
|
from _emerge.Scheduler import Scheduler
|
|
from _emerge.clear_caches import clear_caches
|
|
from _emerge.unmerge import unmerge
|
|
from _emerge.Blocker import Blocker
|
|
|
|
import portage.versions
|
|
import portage.dep
|
|
import portage
|
|
|
|
|
|
class PackageBuilder(object):
|
|
"""
|
|
Portage Package builder class
|
|
"""
|
|
|
|
DEFAULT_PORTAGE_SYNC_CMD = "emerge --sync"
|
|
PORTAGE_SYNC_CMD = shlex.split(os.getenv("MATTER_PORTAGE_SYNC_CMD",
|
|
DEFAULT_PORTAGE_SYNC_CMD))
|
|
|
|
DEFAULT_OVERLAYS_SYNC_CMD = "layman -S"
|
|
OVERLAYS_SYNC_CMD = shlex.split(os.getenv("MATTER_OVERLAYS_SYNC_CMD",
|
|
DEFAULT_OVERLAYS_SYNC_CMD))
|
|
|
|
DEFAULT_PORTAGE_BUILD_ARGS = "--verbose --nospinner"
|
|
PORTAGE_BUILD_ARGS = os.getenv("MATTER_PORTAGE_BUILD_ARGS",
|
|
DEFAULT_PORTAGE_BUILD_ARGS).split()
|
|
|
|
PORTAGE_BUILTIN_ARGS = ["--accept-properties=-interactive"]
|
|
|
|
def __init__(self, emerge_config, packages, params,
|
|
spec_number, tot_spec, pkg_number, tot_pkgs):
|
|
self._emerge_config = emerge_config
|
|
self._packages = packages
|
|
self._params = params
|
|
self._spec_number = spec_number
|
|
self._tot_spec = tot_spec
|
|
self._pkg_number = pkg_number
|
|
self._tot_pkgs = tot_pkgs
|
|
self._built_packages = []
|
|
self._not_found_packages = []
|
|
self._not_installed_packages = []
|
|
self._not_merged_packages = []
|
|
|
|
@classmethod
|
|
def _build_standard_environment(cls, repository=None):
|
|
env = os.environ.copy()
|
|
if repository is not None:
|
|
env["MATTER_REPOSITORY_ID"] = repository
|
|
return env
|
|
|
|
@classmethod
|
|
def setup(cls, executable_hook_f, cwd):
|
|
|
|
# ignore exit status
|
|
subprocess.call(["env-update"])
|
|
|
|
hook_name = executable_hook_f.name
|
|
if not hook_name.endswith("/"):
|
|
# complete with current directory
|
|
hook_name = os.path.join(cwd, hook_name)
|
|
|
|
print_info("spawning pre hook: %s" % (hook_name,))
|
|
return subprocess.call([hook_name],
|
|
env = PackageBuilder._build_standard_environment())
|
|
|
|
@classmethod
|
|
def teardown(cls, executable_hook_f, cwd, exit_st):
|
|
hook_name = executable_hook_f.name
|
|
if not hook_name.endswith("/"):
|
|
# complete with current directory
|
|
hook_name = os.path.join(cwd, hook_name)
|
|
|
|
print_info("spawning post hook: %s, passing exit status: %d" % (
|
|
hook_name, exit_st,))
|
|
env = PackageBuilder._build_standard_environment()
|
|
env["MATTER_EXIT_STATUS"] = str(exit_st)
|
|
return subprocess.call([hook_name], env = env)
|
|
|
|
def _build_execution_header_output(self):
|
|
"""
|
|
Return a string used as stdout/stderr header text.
|
|
"""
|
|
my_str = "{%s of %s particles | %s of %s packages} " % (
|
|
darkgreen(str(self._spec_number)),
|
|
purple(str(self._tot_spec)),
|
|
darkgreen(str(self._pkg_number)),
|
|
purple(str(self._tot_pkgs)),)
|
|
return my_str
|
|
|
|
def get_built_packages(self):
|
|
"""
|
|
Return the list of successfully built packages.
|
|
"""
|
|
return self._built_packages
|
|
|
|
def get_not_found_packages(self):
|
|
"""
|
|
Return the list of packages that haven't been found in Portage.
|
|
"""
|
|
return self._not_found_packages
|
|
|
|
def get_not_installed_packages(self):
|
|
"""
|
|
Return the list of packages that haven't been found on the System.
|
|
"""
|
|
return self._not_installed_packages
|
|
|
|
def get_not_merged_packages(self):
|
|
"""
|
|
Return the list of packages that haven't been able to compile.
|
|
"""
|
|
return self._not_merged_packages
|
|
|
|
def run(self):
|
|
"""
|
|
Execute Package building action.
|
|
"""
|
|
header = self._build_execution_header_output()
|
|
print_info(
|
|
header + "spawning package build: %s" % (
|
|
" ".join(self._packages),))
|
|
|
|
std_env = PackageBuilder._build_standard_environment(
|
|
repository=self._params["repository"])
|
|
|
|
matter_package_names = " ".join(self._packages)
|
|
std_env["MATTER_PACKAGE_NAMES"] = matter_package_names
|
|
print_info("MATTER_PACKAGE_NAMES = %s" % (matter_package_names,))
|
|
|
|
# run pkgpre, if any
|
|
pkgpre = self._params["pkgpre"]
|
|
if pkgpre is not None:
|
|
print_info("spawning --pkgpre: %s" % (pkgpre,))
|
|
tmp_fd, tmp_path = mkstemp()
|
|
with os.fdopen(tmp_fd, "wb") as tmp_f:
|
|
with open(pkgpre, "rb") as pkgpre_f:
|
|
tmp_f.write(pkgpre_f.read())
|
|
try:
|
|
# now execute
|
|
os.chmod(tmp_path, 0o700)
|
|
exit_st = subprocess.call([tmp_path], env = std_env)
|
|
if exit_st != 0:
|
|
return exit_st
|
|
finally:
|
|
os.remove(tmp_path)
|
|
|
|
dirs_cleanup = []
|
|
exit_st = self._run_builder(dirs_cleanup)
|
|
|
|
print_info("builder terminated, exit status: %d" % (exit_st,))
|
|
|
|
# cleanup temporary directories registered on the queue
|
|
for tmp_dir in dirs_cleanup:
|
|
self.__cleanup_dir(tmp_dir)
|
|
|
|
# run pkgpost, if any
|
|
pkgpost = self._params["pkgpost"]
|
|
if pkgpost is not None:
|
|
print_info("spawning --pkgpost: %s" % (pkgpost,))
|
|
tmp_fd, tmp_path = mkstemp()
|
|
with os.fdopen(tmp_fd, "wb") as tmp_f:
|
|
with open(pkgpost, "rb") as pkgpost_f:
|
|
tmp_f.write(pkgpost_f.read())
|
|
try:
|
|
# now execute
|
|
os.chmod(tmp_path, 0o700)
|
|
post_exit_st = subprocess.call([tmp_path, str(exit_st)],
|
|
env = std_env)
|
|
if post_exit_st != 0:
|
|
return post_exit_st
|
|
finally:
|
|
os.remove(tmp_path)
|
|
|
|
return exit_st
|
|
|
|
def __cleanup_dir(self, tmp_dir):
|
|
if os.path.isdir(tmp_dir) \
|
|
and (not os.path.islink(tmp_dir)):
|
|
shutil.rmtree(tmp_dir, True)
|
|
|
|
def _pre_graph_filters(self, package, portdb, vardb):
|
|
"""
|
|
Execute basic, pre-graph generation (dependencies calculation)
|
|
filters against the package dependency to see if it's eligible
|
|
for the graph.
|
|
"""
|
|
allow_rebuild = self._params['rebuild'] == "yes"
|
|
allow_not_installed = self._params['not-installed'] == "yes"
|
|
allow_downgrade = self._params['downgrade'] == "yes"
|
|
|
|
best_visible = portdb.xmatch("bestmatch-visible", package)
|
|
if not best_visible:
|
|
# package not found, return error
|
|
print_error("cannot match: %s, ignoring this one" % (package,))
|
|
self._not_found_packages.append(package)
|
|
return None
|
|
|
|
print_info("matched: %s for %s" % (best_visible, package,))
|
|
# now determine what's the installed version.
|
|
best_installed = portage.best(vardb.match(package))
|
|
if (not best_installed) and (not allow_not_installed):
|
|
# package not installed
|
|
print_error("package not installed: %s, ignoring this one" % (
|
|
package,))
|
|
self._not_installed_packages.append(package)
|
|
return None
|
|
|
|
if (not best_installed) and allow_not_installed:
|
|
print_warning(
|
|
"package not installed: "
|
|
"%s, but 'not-installed: yes' provided" % (package,))
|
|
|
|
cmp_res = -1
|
|
if best_installed:
|
|
print_info("found installed: %s for %s" % (
|
|
best_installed, package,))
|
|
# now compare
|
|
# -1 if best_installed is older than best_visible
|
|
# 1 if best_installed is newer than best_visible
|
|
# 0 if they are equal
|
|
cmp_res = portage.versions.pkgcmp(
|
|
portage.versions.pkgsplit(best_installed),
|
|
portage.versions.pkgsplit(best_visible))
|
|
|
|
is_rebuild = cmp_res == 0
|
|
|
|
if (cmp_res == 1) and (not allow_downgrade):
|
|
# downgrade in action and downgrade not allowed, aborting!
|
|
print_warning(
|
|
"package: %s, would be downgraded, %s to %s, ignoring" % (
|
|
package, best_installed, best_visible,))
|
|
return None
|
|
|
|
if is_rebuild and (not allow_rebuild):
|
|
# rebuild in action and rebuild not allowed, aborting!
|
|
print_warning(
|
|
"package: %s, would be rebuilt to %s, ignoring" % (
|
|
package, best_visible,))
|
|
return None
|
|
|
|
# at this point we can go ahead accepting package in queue
|
|
print_info("package: %s [%s], accepted in queue" % (
|
|
best_visible, package,))
|
|
return best_visible
|
|
|
|
def _post_graph_filters(self, graph, vardb, portdb):
|
|
"""
|
|
Execute post-graph generation (dependencies calculation)
|
|
filters against the package dependencies to see if they're
|
|
eligible for building.
|
|
"""
|
|
# list of _emerge.Package.Package objects
|
|
package_queue = graph.altlist()
|
|
|
|
# filter out blockers
|
|
real_queue = [x for x in package_queue if not isinstance(
|
|
x, Blocker)]
|
|
# filter out broken or corrupted objects
|
|
real_queue = [x for x in real_queue if x.cpv]
|
|
|
|
# package_queue can also contain _emerge.Blocker.Blocker objects
|
|
# not exposing .cpv field (but just .cp).
|
|
dep_list = []
|
|
for pobj in package_queue:
|
|
if isinstance(pobj, Blocker):
|
|
# blocker, list full atom
|
|
dep_list.append("!" + pobj.atom)
|
|
continue
|
|
cpv = pobj.cpv
|
|
repo = pobj.repo
|
|
if repo:
|
|
repo = "::" + repo
|
|
if cpv:
|
|
dep_list.append(cpv+repo)
|
|
else:
|
|
print_warning(
|
|
"attention, %s has broken cpv: '%s', ignoring" % (
|
|
pobj, cpv,))
|
|
|
|
# calculate dependencies, if --dependencies is not enabled
|
|
# because we have to validate it
|
|
if (self._params['dependencies'] == "no") \
|
|
and (len(package_queue) > 1):
|
|
deps = "\n ".join(dep_list)
|
|
print_warning("dependencies pulled in:")
|
|
print_warning(deps)
|
|
print_warning("but 'dependencies: no' in config, aborting")
|
|
return None
|
|
|
|
# inspect use flags changes
|
|
allow_new_useflags = self._params['new-useflags'] == "yes"
|
|
allow_removed_useflags = \
|
|
self._params['removed-useflags'] == "yes"
|
|
|
|
use_flags_give_up = False
|
|
if (not allow_new_useflags) or (not allow_removed_useflags):
|
|
# checking for use flag changes
|
|
for pkg in real_queue:
|
|
# frozenset
|
|
enabled_flags = pkg.use.enabled
|
|
inst_atom = portage.best(vardb.match(pkg.slot_atom))
|
|
if not inst_atom:
|
|
# new package, ignore check
|
|
continue
|
|
installed_flags = frozenset(
|
|
vardb.aux_get(inst_atom, ["USE"])[0].split())
|
|
|
|
new_flags = enabled_flags - installed_flags
|
|
removed_flags = installed_flags - enabled_flags
|
|
|
|
if (not allow_new_useflags) and new_flags:
|
|
print_warning(
|
|
"ouch: %s wants these new USE flags: %s" % (
|
|
pkg.cpv+"::"+pkg.repo,
|
|
" ".join(sorted(new_flags)),))
|
|
use_flags_give_up = True
|
|
if (not allow_removed_useflags) and removed_flags:
|
|
print_warning(
|
|
"ouch: %s has these USE flags removed: %s" % (
|
|
pkg.cpv+"::"+pkg.repo,
|
|
" ".join(sorted(removed_flags)),))
|
|
use_flags_give_up = True
|
|
|
|
if use_flags_give_up:
|
|
print_warning("cannot continue due to unmet "
|
|
"USE flags constraint")
|
|
return None
|
|
|
|
allow_downgrade = self._params['downgrade'] == "yes"
|
|
# check the whole queue against downgrade directive
|
|
if not allow_downgrade:
|
|
allow_downgrade_give_ups = []
|
|
for pkg in real_queue:
|
|
inst_atom = portage.best(vardb.match(pkg.slot_atom))
|
|
cmp_res = -1
|
|
if inst_atom:
|
|
# -1 if inst_atom is older than pkg.cpv
|
|
# 1 if inst_atom is newer than pkg.cpv
|
|
# 0 if they are equal
|
|
cmp_res = portage.versions.pkgcmp(
|
|
portage.versions.pkgsplit(inst_atom),
|
|
portage.versions.pkgsplit(pkg.cpv))
|
|
if cmp_res > 0:
|
|
allow_downgrade_give_ups.append((inst_atom, pkg.cpv))
|
|
|
|
if allow_downgrade_give_ups:
|
|
print_warning(
|
|
"cannot continue due to package "
|
|
"downgrade not allowed for:")
|
|
for inst_atom, avail_atom in allow_downgrade_give_ups:
|
|
print_warning(" installed: %s | wanted: %s" % (
|
|
inst_atom, avail_atom,))
|
|
return None
|
|
|
|
changing_repo_pkgs = []
|
|
for pkg in real_queue:
|
|
wanted_repo = pkg.repo
|
|
inst_atom = portage.best(vardb.match(pkg.slot_atom))
|
|
current_repo = vardb.aux_get(inst_atom, ["repository"])[0]
|
|
if current_repo:
|
|
if current_repo != wanted_repo:
|
|
changing_repo_pkgs.append(
|
|
(pkg.cpv, pkg.slot, current_repo, wanted_repo))
|
|
|
|
if changing_repo_pkgs:
|
|
print_warning("")
|
|
print_warning(
|
|
"Attention, packages are moving across SPM repositories:")
|
|
for pkg_atom, pkg_slot, c_repo, w_repo in changing_repo_pkgs:
|
|
print_warning(" %s:%s [%s->%s]" % (pkg_atom, pkg_slot,
|
|
c_repo, w_repo,))
|
|
print_warning("")
|
|
|
|
allow_spm_repo_change = self._params['spm-repository-change'] \
|
|
== "yes"
|
|
allow_spm_repo_change_if_ups = \
|
|
self._params['spm-repository-change-if-upstreamed'] == "yes"
|
|
|
|
if (not allow_spm_repo_change) and allow_spm_repo_change_if_ups:
|
|
print_info("SPM repository change allowed if the original "
|
|
"repository does no longer contain "
|
|
"current packages.")
|
|
|
|
# check if source repository still contains the package
|
|
# in this case, set allow_spm_repo_change to True
|
|
_allow = True
|
|
for pkg_atom, pkg_slot, c_repo, w_repo in changing_repo_pkgs:
|
|
pkg_key = portage.dep.dep_getkey("=%s" % (pkg_atom,))
|
|
pkg_target = "%s:%s::%s" % (
|
|
pkg_key, pkg_slot, c_repo)
|
|
pkg_match = portdb.xmatch("bestmatch-visible", pkg_target)
|
|
if pkg_match:
|
|
# package still available in source repo
|
|
_allow = False
|
|
print_warning(" %s:%s, still in repo: %s" % (
|
|
pkg_atom, pkg_slot, c_repo,))
|
|
# do not break, print all the list
|
|
# break
|
|
|
|
if _allow and changing_repo_pkgs:
|
|
print_info(
|
|
"current packages are no longer in their "
|
|
"original repository, SPM repository change allowed.")
|
|
allow_spm_repo_change = True
|
|
|
|
if changing_repo_pkgs and (not allow_spm_repo_change):
|
|
print_warning(
|
|
"cannot continue due to unmet SPM repository "
|
|
"change constraint")
|
|
return None
|
|
|
|
print_info("USE flags constraints are met for all "
|
|
"the queued packages")
|
|
return dep_list, real_queue
|
|
|
|
def _setup_keywords(self, settings):
|
|
"""
|
|
Setup ACCEPT_KEYWORDS for package.
|
|
"""
|
|
# setup stable keywords if needed
|
|
force_stable_keywords = self._params["stable"] == "yes"
|
|
inherit_keywords = self._params["stable"] == "inherit"
|
|
|
|
settings.unlock()
|
|
arch = settings["ARCH"][:]
|
|
orig_key = "ACCEPT_KEYWORDS_MATTER"
|
|
orig_keywords = settings.get(orig_key)
|
|
|
|
if orig_keywords is None:
|
|
orig_keywords = settings["ACCEPT_KEYWORDS"][:]
|
|
settings[orig_key] = orig_keywords
|
|
settings.backup_changes(orig_key)
|
|
|
|
if force_stable_keywords:
|
|
keywords = arch
|
|
elif inherit_keywords:
|
|
keywords = orig_keywords
|
|
else:
|
|
keywords = "%s ~%s" % (arch, arch)
|
|
|
|
settings.unlock()
|
|
settings["ACCEPT_KEYWORDS"] = keywords
|
|
settings.backup_changes("ACCEPT_KEYWORDS")
|
|
settings.lock()
|
|
|
|
def _run_builder(self, dirs_cleanup_queue):
|
|
"""
|
|
This method is called by _run and executes the whole package build
|
|
logic, including constraints validation given by argv parameters.
|
|
NOTE: negative errors indicate warnings that can be skipped.
|
|
"""
|
|
if self._packages:
|
|
first_package = self._packages[0]
|
|
else:
|
|
first_package = "_empty_"
|
|
|
|
log_dir = mkdtemp(prefix="matter_build.",
|
|
suffix="." + first_package.replace("/", "_").lstrip("<>=~"))
|
|
dirs_cleanup_queue.append(log_dir)
|
|
|
|
emerge_settings, emerge_trees, mtimedb = self._emerge_config
|
|
|
|
# Setup stable/unstable keywords, must be done on
|
|
# emerge_settings bacause the reference is spread everywhere
|
|
# in emerge_trees.
|
|
# This is not thread-safe, but Portage isn't either, so
|
|
# who cares!
|
|
self._setup_keywords(emerge_settings)
|
|
|
|
settings = portage.config(clone=emerge_settings)
|
|
|
|
portdb = emerge_trees[settings["ROOT"]]["porttree"].dbapi
|
|
if not portdb.frozen:
|
|
portdb.freeze()
|
|
vardb = emerge_trees[settings["ROOT"]]["vartree"].dbapi
|
|
vardb.settings.unlock()
|
|
vardb.settings["PORT_LOGDIR"] = log_dir
|
|
vardb.settings.backup_changes("PORT_LOGDIR")
|
|
vardb.settings.lock()
|
|
|
|
# Load the most current variables from /etc/profile.env, which
|
|
# has been re-generated by the env-update call in _run()
|
|
settings.unlock()
|
|
settings.reload()
|
|
settings.regenerate()
|
|
settings.lock()
|
|
|
|
packages = []
|
|
# execute basic, pre-graph generation filters against each
|
|
# package dependency in self._packages.
|
|
# This is just fast pruning of obvious obviousness.
|
|
for package in self._packages:
|
|
best_visible = self._pre_graph_filters(
|
|
package, portdb, vardb)
|
|
if best_visible is not None:
|
|
packages.append((package, best_visible))
|
|
|
|
if not packages:
|
|
print_warning("No remaining packages in queue, aborting.")
|
|
return 0
|
|
|
|
# at this point we can go ahead building packages
|
|
print_info("starting to build:")
|
|
for package, best_visible in packages:
|
|
print_info(": %s -> %s" % (
|
|
package, best_visible,))
|
|
|
|
if not getcolor():
|
|
portage.output.nocolor()
|
|
|
|
# non interactive properties, this is not really required
|
|
# accept-properties just sets os.environ...
|
|
build_args = []
|
|
build_args += PackageBuilder.PORTAGE_BUILD_ARGS
|
|
build_args += PackageBuilder.PORTAGE_BUILTIN_ARGS
|
|
build_args += ["=" + best_v for _x, best_v in packages]
|
|
myaction, myopts, myfiles = parse_opts(build_args)
|
|
|
|
if "--pretend" in myopts:
|
|
print_warning("cannot use --pretend emerge argument, you idiot")
|
|
del myopts["--pretend"]
|
|
if "--ask" in myopts:
|
|
print_warning("cannot use --ask emerge argument, you idiot")
|
|
del myopts["--ask"]
|
|
spinner = stdout_spinner()
|
|
if "--quiet" in myopts:
|
|
spinner.update = spinner.update_basic
|
|
elif "--nospinner" in myopts:
|
|
spinner.update = spinner.update_basic
|
|
if settings.get("TERM") == "dumb" or not is_stdout_a_tty():
|
|
spinner.update = spinner.update_basic
|
|
|
|
print_info("emerge args: %s" % (" ".join(build_args),))
|
|
|
|
params = create_depgraph_params(myopts, myaction)
|
|
success, graph, favorites = backtrack_depgraph(settings,
|
|
emerge_trees, myopts, params, myaction, myfiles, spinner)
|
|
|
|
if not success:
|
|
# print issues to stdout and give up
|
|
print_warning("dependencies calculation failed, aborting")
|
|
graph.display_problems()
|
|
return 0
|
|
print_info("dependency graph generated successfully")
|
|
|
|
f_data = self._post_graph_filters(graph, vardb, portdb)
|
|
if f_data is None:
|
|
# post-graph filters not passed, giving up
|
|
return 0
|
|
dep_list, real_queue = f_data
|
|
|
|
print_info("about to build the following packages:")
|
|
for dep in dep_list:
|
|
print_info(" %s" % (dep,))
|
|
|
|
# re-calling action_build(), deps are re-calculated though
|
|
validate_ebuild_environment(emerge_trees)
|
|
mergetask = Scheduler(settings, emerge_trees, mtimedb,
|
|
myopts, spinner, favorites=favorites,
|
|
graph_config=graph.schedulerGraph())
|
|
del graph
|
|
clear_caches(emerge_trees)
|
|
retval = mergetask.merge()
|
|
|
|
not_merged = []
|
|
real_queue_map = dict((pkg.cpv, pkg) for pkg in real_queue)
|
|
failed_package = None
|
|
if retval != 0:
|
|
merge_list = mtimedb.get("resume", {}).get("mergelist")
|
|
for _merge_type, _merge_root, merge_atom, _merge_act in merge_list:
|
|
if failed_package is None:
|
|
# we consider the first encountered package the one
|
|
# that failed. It makes sense since packages are built
|
|
# serially as of today.
|
|
# Also, the package object must be available in our
|
|
# package queue, so grab it from there.
|
|
failed_package = real_queue_map.get(merge_atom)
|
|
not_merged.append(merge_atom)
|
|
self._not_merged_packages.append(merge_atom)
|
|
|
|
for pkg in real_queue:
|
|
cpv = pkg.cpv
|
|
if not cpv:
|
|
print_warning("package: %s, has broken cpv: '%s', ignoring" % (
|
|
pkg, cpv,))
|
|
elif cpv not in not_merged:
|
|
# add to build queue
|
|
print_info("package: %s, successfully built" % (cpv,))
|
|
self._built_packages.append(cpv)
|
|
|
|
post_emerge(myaction, myopts, myfiles, settings["ROOT"],
|
|
emerge_trees, mtimedb, retval)
|
|
|
|
subprocess.call(["env-update"])
|
|
|
|
if failed_package is not None:
|
|
print_warning("failed package: %s::%s" % (failed_package.cpv,
|
|
failed_package.repo,))
|
|
|
|
if self._params['buildfail'] and (failed_package is not None):
|
|
|
|
std_env = PackageBuilder._build_standard_environment(
|
|
repository=self._params["repository"])
|
|
std_env["MATTER_PACKAGE_NAMES"] = " ".join(self._packages)
|
|
std_env["MATTER_PORTAGE_FAILED_PACKAGE_NAME"] = failed_package.cpv
|
|
std_env["MATTER_PORTAGE_REPOSITORY"] = failed_package.repo
|
|
# call pkgfail hook if defined
|
|
std_env["MATTER_PORTAGE_BUILD_LOG_DIR"] = os.path.join(log_dir,
|
|
"build")
|
|
|
|
buildfail = self._params['buildfail']
|
|
print_info("spawning buildfail: %s" % (buildfail,))
|
|
tmp_fd, tmp_path = mkstemp()
|
|
with os.fdopen(tmp_fd, "wb") as tmp_f:
|
|
with open(buildfail, "rb") as buildfail_f:
|
|
tmp_f.write(buildfail_f.read())
|
|
try:
|
|
# now execute
|
|
os.chmod(tmp_path, 0o700)
|
|
exit_st = subprocess.call([tmp_path], env = std_env)
|
|
if exit_st != 0:
|
|
return exit_st
|
|
finally:
|
|
os.remove(tmp_path)
|
|
|
|
print_info("portage spawned, return value: %d" % (retval,))
|
|
return retval
|
|
|
|
@classmethod
|
|
def post_build(cls, emerge_config):
|
|
"""
|
|
Execute Portage post-build tasks.
|
|
"""
|
|
emerge_settings, emerge_trees, mtimedb = emerge_config
|
|
if "yes" == emerge_settings.get("AUTOCLEAN"):
|
|
print_info("executing post-build operations, please wait...")
|
|
builtin_args = PackageBuilder.PORTAGE_BUILTIN_ARGS
|
|
_action, opts, _files = parse_opts(
|
|
PackageBuilder.PORTAGE_BUILD_ARGS + builtin_args)
|
|
unmerge(emerge_trees[emerge_settings["ROOT"]]["root_config"],
|
|
opts, "clean", [], mtimedb["ldpath"], autoclean=1)
|
|
|
|
@classmethod
|
|
def sync(cls):
|
|
"""
|
|
Execute Portage and Overlays sync
|
|
"""
|
|
sync_cmd = PackageBuilder.PORTAGE_SYNC_CMD
|
|
std_env = PackageBuilder._build_standard_environment()
|
|
exit_st = subprocess.call(sync_cmd, env = std_env)
|
|
if exit_st != 0:
|
|
return exit_st
|
|
|
|
# overlays update
|
|
overlay_cmd = PackageBuilder.OVERLAYS_SYNC_CMD
|
|
return subprocess.call(overlay_cmd, env = std_env)
|