[entropy] goodbye etpConst['spm'], implement new SpmPlugin methods, bump SpmPlugin API

This commit is contained in:
Fabio Erculiani
2009-12-03 16:59:44 +01:00
parent 06adc752c0
commit 99ee02f20c
14 changed files with 787 additions and 477 deletions
@@ -453,7 +453,7 @@ class ClientSystemSettingsPlugin(SystemSettingsPlugin):
'filesbackup': etpConst['filesbackup'],
'forcedupdates': etpConst['forcedupdates'],
'packagehashes': etpConst['packagehashes'],
'ignore_spm_downgrades': etpConst['spm']['ignore-spm-downgrades'],
'ignore_spm_downgrades': False,
'collisionprotect': etpConst['collisionprotect'],
'configprotect': etpConst['configprotect'][:],
'configprotectmask': etpConst['configprotectmask'][:],
+2 -2
View File
@@ -24,7 +24,7 @@ class CalculatorsMixin:
# get all the installed packages
installed_packages = dbconn.listAllIdpackages()
pdepend_id = etpConst['spm']['pdepend_id']
pdepend_id = etpConst['dependency_type_ids']['pdepend_id']
deps_not_matched = set()
# now look
length = len(installed_packages)
@@ -1553,7 +1553,7 @@ class CalculatorsMixin:
monotree = set(idpackages) # monodimensional tree
# post-dependencies won't be pulled in
pdepend_id = etpConst['spm']['pdepend_id']
pdepend_id = etpConst['dependency_type_ids']['pdepend_id']
# check if dependstable is sane before beginning
self.clientDbconn.retrieveReverseDependencies(idpackages[0])
count = 0
+13 -203
View File
@@ -1146,18 +1146,19 @@ class Repository:
self.Entropy.update_repository_revision(repo)
if self.Entropy.indexing:
self.do_database_indexing(repo)
def_repoid = my_repos['default_repository']
if repo == def_repoid:
try:
self.run_config_files_updates(repo)
except Exception as e:
entropy.tools.print_traceback()
mytxt = "%s: %s" % (
blue(_("Configuration files update error, not critical, continuing")),
darkred(repr(e)),
)
self.Entropy.updateProgress(mytxt, importance = 0,
type = "info", header = blue(" # "),)
try:
spm_class = self.Entropy.Spm_class()
spm_class.entropy_post_repository_update_hook(self.Entropy,
repo)
except Exception as e:
entropy.tools.print_traceback()
mytxt = "%s: %s" % (
blue(_("Configuration files update error, not critical, continuing")),
darkred(repr(e)),
)
self.Entropy.updateProgress(mytxt, importance = 0,
type = "info", header = blue(" # "),)
# execute post update repo hook
self._run_post_update_repository_hook(repo)
@@ -1207,197 +1208,6 @@ class Repository:
return 0
def run_config_files_updates(self, repo):
# are we root?
if etpConst['uid'] != 0:
self.Entropy.updateProgress(
brown(_("Skipping configuration files update, you are not root.")),
importance = 1,
type = "info",
header = blue(" @@ ")
)
return
# make.conf
self._config_updates_make_conf(repo)
self._config_updates_make_profile(repo)
def _config_updates_make_conf(self, repo):
## WARNING: it doesn't handle multi-line variables, yet. remember this.
system_make_conf = etpConst['spm']['global_make_conf']
avail_data = self.Entropy.SystemSettings['repositories']['available']
repo_dbpath = avail_data[repo]['dbpath']
repo_make_conf = os.path.join(repo_dbpath,
os.path.basename(system_make_conf))
if not (os.path.isfile(repo_make_conf) and \
os.access(repo_make_conf, os.R_OK)):
return
make_conf_variables_check = ["CHOST"]
if not os.path.isfile(system_make_conf):
self.Entropy.updateProgress(
"%s %s. %s." % (
red(system_make_conf),
blue(_("does not exist")), blue(_("Overwriting")),
),
importance = 1,
type = "info",
header = blue(" @@ ")
)
if os.path.lexists(system_make_conf):
shutil.move(
system_make_conf,
"%s.backup_%s" % (system_make_conf,
entropy.tools.get_random_number(),)
)
shutil.copy2(repo_make_conf, system_make_conf)
elif os.access(system_make_conf, os.W_OK):
repo_f = open(repo_make_conf, "r")
sys_f = open(system_make_conf, "r")
repo_make_c = [x.strip() for x in repo_f.readlines()]
sys_make_c = [x.strip() for x in sys_f.readlines()]
repo_f.close()
sys_f.close()
# read repository settings
repo_data = {}
for setting in make_conf_variables_check:
for line in repo_make_c:
if line.startswith(setting+"="):
# there can't be bash vars with a space
# after its name on declaration
repo_data[setting] = line
# I don't break, because there might be
# other overlapping settings
differences = {}
# update make.conf data in memory
for setting in repo_data:
for idx in range(len(sys_make_c)):
line = sys_make_c[idx]
if line.startswith(setting+"=") and \
(line != repo_data[setting]):
# there can't be bash vars with a
# space after its name on declaration
self.Entropy.updateProgress(
"%s: %s %s. %s." % (
red(system_make_conf), bold(repr(setting)),
blue(_("variable differs")), red(_("Updating")),
),
importance = 1,
type = "info",
header = blue(" @@ ")
)
differences[setting] = repo_data[setting]
line = repo_data[setting]
sys_make_c[idx] = line
if differences:
self.Entropy.updateProgress(
"%s: %s." % (
red(system_make_conf),
blue(_("updating critical variables")),
),
importance = 1,
type = "info",
header = blue(" @@ ")
)
# backup user make.conf
shutil.copy2(system_make_conf,
"%s.entropy_backup" % (system_make_conf,))
self.Entropy.updateProgress(
"%s: %s." % (
red(system_make_conf),
darkgreen("writing changes to disk"),
),
importance = 1,
type = "info",
header = blue(" @@ ")
)
# write to disk, safely
tmp_make_conf = "%s.entropy_write" % (system_make_conf,)
f = open(tmp_make_conf, "w")
for line in sys_make_c: f.write(line+"\n")
f.flush()
f.close()
shutil.move(tmp_make_conf, system_make_conf)
# update environment
for var in differences:
try:
myval = '='.join(differences[var].strip().split("=")[1:])
if myval:
if myval[0] in ("'", '"',): myval = myval[1:]
if myval[-1] in ("'", '"',): myval = myval[:-1]
except IndexError:
myval = ''
os.environ[var] = myval
def _config_updates_make_profile(self, repo):
avail_data = self.Entropy.SystemSettings['repositories']['available']
repo_dbpath = avail_data[repo]['dbpath']
profile_link_name = etpConst['spm']['global_make_profile_link_name']
repo_make_profile = os.path.join(repo_dbpath, profile_link_name)
if not (os.path.isfile(repo_make_profile) and \
os.access(repo_make_profile, os.R_OK)):
return
system_make_profile = etpConst['spm']['global_make_profile']
f = open(repo_make_profile, "r")
repo_profile_link_data = f.readline().strip()
f.close()
current_profile_link = ''
if os.path.islink(system_make_profile) and \
os.access(system_make_profile, os.R_OK):
current_profile_link = os.readlink(system_make_profile)
if (repo_profile_link_data != current_profile_link) and \
repo_profile_link_data:
self.Entropy.updateProgress(
"%s: %s %s. %s." % (
red(system_make_profile), blue("link"),
blue(_("differs")), red(_("Updating")),
),
importance = 1,
type = "info",
header = blue(" @@ ")
)
merge_sfx = ".entropy_merge"
os.symlink(repo_profile_link_data, system_make_profile+merge_sfx)
if entropy.tools.is_valid_path(system_make_profile+merge_sfx):
os.rename(system_make_profile+merge_sfx, system_make_profile)
else:
# revert change, link does not exist yet
self.Entropy.updateProgress(
"%s: %s %s. %s." % (
red(system_make_profile), blue("new link"),
blue(_("does not exist")), red(_("Reverting")),
),
importance = 1,
type = "info",
header = blue(" @@ ")
)
os.remove(system_make_profile+merge_sfx)
def check_entropy_updates(self):
rc = False
if not self.noEquoCheck:
+24 -43
View File
@@ -17,18 +17,11 @@ from entropy.const import *
from entropy.exceptions import *
from entropy.output import *
from entropy.i18n import _
import entropy.tools
class Trigger:
VALID_PHASES = ("preinstall", "postinstall", "preremove", "postremove",)
ENV_VARS_DIR = etpConst['spm']['env_dir_reference']
ENV_UPDATE_HOOK = etpConst['spm']['env_update_cmd']
PHASES = {
'preinstall': "preinstall",
'postinstall': "postinstall",
'preremove': "preremove",
'postremove': "postremove",
}
import entropy.tools as entropyTools
def __init__(self, entropy_client, phase, pkgdata, package_action = None):
@@ -89,22 +82,20 @@ class Trigger:
functions = []
if self.spm_support:
spm_class = self.Entropy.Spm_class()
phases_map = spm_class.package_phases_map()
while True:
if self.pkgdata['spm_phases'] != None:
if etpConst['spm']['postinst_phase'] not \
if self.pkgdata['spm_phases'] is not None:
if phases_map.get('postinstall') not \
in self.pkgdata['spm_phases']:
break
functions.append(self.trigger_spm_postinstall)
break
cont_dirs = set((os.path.dirname(x) for x in self.pkgdata['content']))
if Trigger.ENV_VARS_DIR in cont_dirs:
ldpaths = entropy.tools.collect_linker_paths()
if len(cont_dirs) != len(cont_dirs - set(ldpaths)):
functions.append(self.trigger_env_update)
else:
ldpaths = self.Entropy.entropyTools.collect_linker_paths()
if len(cont_dirs) != len(cont_dirs - set(ldpaths)):
functions.append(self.trigger_env_update)
if self.pkgdata['trigger']:
functions.append(self.trigger_call_ext_postinstall)
@@ -117,9 +108,11 @@ class Trigger:
# Portage phases
if self.spm_support:
spm_class = self.Entropy.Spm_class()
phases_map = spm_class.package_phases_map()
while True:
if self.pkgdata['spm_phases'] != None:
if etpConst['spm']['preinst_phase'] not \
if phases_map.get('preinstall') not \
in self.pkgdata['spm_phases']:
break
functions.append(self.trigger_spm_preinstall)
@@ -137,12 +130,9 @@ class Trigger:
cont_dirs = set((os.path.dirname(x) for x in \
self.pkgdata['removecontent']))
if Trigger.ENV_VARS_DIR in cont_dirs:
ldpaths = entropy.tools.collect_linker_paths()
if len(cont_dirs) != len(cont_dirs - set(ldpaths)):
functions.append(self.trigger_env_update)
else:
ldpaths = self.Entropy.entropyTools.collect_linker_paths()
if len(cont_dirs) != len(cont_dirs - set(ldpaths)):
functions.append(self.trigger_env_update)
if self.pkgdata['trigger']:
functions.append(self.trigger_call_ext_postremove)
@@ -156,10 +146,12 @@ class Trigger:
# Portage hook
if self.spm_support:
spm_class = self.Entropy.Spm_class()
phases_map = spm_class.package_phases_map()
while True:
if self.pkgdata['spm_phases'] != None:
if etpConst['spm']['prerm_phase'] not \
if phases_map.get('preremove') not \
in self.pkgdata['spm_phases']:
break
functions.append(self.trigger_spm_preremove)
@@ -169,7 +161,7 @@ class Trigger:
# in place and also because doesn't make any difference
while True:
if self.pkgdata['spm_phases'] != None:
if etpConst['spm']['postrm_phase'] not \
if phases_map.get('postremove') not \
in self.pkgdata['spm_phases']:
break
functions.append(self.trigger_spm_postremove)
@@ -386,7 +378,7 @@ class Trigger:
tg_pfx = "%s/trigger-" % (etpConst['entropyunpackdir'],)
while True:
triggerfile = "%s%s" % (tg_pfx, self.Entropy.entropyTools.get_random_number(),)
triggerfile = "%s%s" % (tg_pfx, entropy.tools.get_random_number(),)
if not os.path.isfile(triggerfile): break
triggerdir = os.path.dirname(triggerfile)
@@ -422,20 +414,13 @@ class Trigger:
return my.run(self.phase, self.pkgdata, triggerfile)
def trigger_env_update(self):
self.Entropy.clientLog.log(
ETP_LOGPRI_INFO,
ETP_LOGLEVEL_NORMAL,
"[POST] Running env-update"
"[POST] Running env_update"
)
kwargs = {}
if etpUi['mute']:
kwargs['stdout'] = self.Entropy.clientLog
kwargs['stderr'] = self.Entropy.clientLog
args = (Trigger.ENV_UPDATE_HOOK,)
proc = subprocess.Popen(args, **kwargs)
proc.wait()
self.Spm.environment_update(stdin = self.Entropy.clientLog,
stdout = self.Entropy.clientLog)
def trigger_spm_postinstall(self):
@@ -444,8 +429,7 @@ class Trigger:
importance = 0,
header = red(" ## ")
)
return self.Spm.execute_package_phase(self.pkgdata,
Trigger.PHASES['postinstall'])
return self.Spm.execute_package_phase(self.pkgdata, 'postinstall')
def trigger_spm_preinstall(self):
@@ -454,8 +438,7 @@ class Trigger:
importance = 0,
header = red(" ## ")
)
return self.Spm.execute_package_phase(self.pkgdata,
Trigger.PHASES['preinstall'])
return self.Spm.execute_package_phase(self.pkgdata, 'preinstall')
def trigger_spm_preremove(self):
@@ -464,8 +447,7 @@ class Trigger:
importance = 0,
header = red(" ## ")
)
return self.Spm.execute_package_phase(self.pkgdata,
Trigger.PHASES['preremove'])
return self.Spm.execute_package_phase(self.pkgdata, 'preremove')
def trigger_spm_postremove(self):
@@ -474,5 +456,4 @@ class Trigger:
importance = 0,
header = red(" ## ")
)
return self.Spm.execute_package_phase(self.pkgdata,
Trigger.PHASES['postremove'])
return self.Spm.execute_package_phase(self.pkgdata, 'postremove')
+2 -60
View File
@@ -507,69 +507,11 @@ def const_default_settings(rootdir):
'default_nice': 0,
# Default download socket timeout for Entropy Client transceivers
'default_download_timeout': 20,
'spm': {
# Entropy package dependencies type identifiers
'dependency_type_ids': {
'(r)depend_id': 0,
'pdepend_id': 1,
'mdepend_id': 2, # actually, this is entropy-only
'ebuild_file_extension': "ebuild",
'preinst_phase': "preinst",
'postinst_phase': "postinst",
'prerm_phase': "prerm",
'postrm_phase': "postrm",
'setup_phase': "setup",
'compile_phase': "compile",
'install_phase': "install",
'unpack_phase': "unpack",
'ebuild_pkg_tag_var': "ENTROPY_PROJECT_TAG",
'global_make_conf': rootdir+"/etc/make.conf",
'global_package_keywords': rootdir+"/etc/portage/package.keywords",
'global_package_use': rootdir+"/etc/portage/package.use",
'global_package_mask': rootdir+"/etc/portage/package.mask",
'global_package_unmask': rootdir+"/etc/portage/package.unmask",
'global_make_profile': rootdir+"/etc/make.profile",
'global_make_profile_link_name': "profile.link",
# source package manager executable
'exec': rootdir+"/usr/bin/emerge",
'env_dir_reference': "/etc/env.d",
'env_update_cmd': "/usr/sbin/env-update",
'source_profile': ["source", "/etc/profile"],
'ask_cmd': "--ask",
'info_cmd': "--info",
'remove_cmd': "-C",
'nodeps_cmd': "--nodeps",
'fetchonly_cmd': "--fetchonly",
'buildonly_cmd': "--buildonly",
'oneshot_cmd': "--oneshot",
'pretend_cmd': "--pretend",
'verbose_cmd': "--verbose",
'nocolor_cmd': "--color=n",
'backend': "portage", # default one
'xpak_entries': {
'description': "DESCRIPTION",
'homepage': "HOMEPAGE",
'chost': "CHOST",
'category': "CATEGORY",
'cflags': "CFLAGS",
'cxxflags': "CXXFLAGS",
'license': "LICENSE",
'src_uri': "SRC_URI",
'use': "USE",
'iuse': "IUSE",
'slot': "SLOT",
'provide': "PROVIDE",
'depend': "DEPEND",
'rdepend': "RDEPEND",
'pdepend': "PDEPEND",
'needed': "NEEDED",
'inherited': "INHERITED",
'keywords': "KEYWORDS",
'contents': "CONTENTS",
'counter': "COUNTER",
'defined_phases': "DEFINED_PHASES",
'pf': "PF",
},
'system_packages': [],
'ignore-spm-downgrades': False,
},
# entropy client packages download speed limit (in kb/sec)
+1 -1
View File
@@ -860,7 +860,7 @@ class SystemSettings(Singleton, EntropyPluginStore):
'proxy': etpConst['proxy'].copy(),
'name': etpConst['systemname'],
'log_level': etpConst['entropyloglevel'],
'spm_backend': etpConst['spm']['backend'],
'spm_backend': None,
}
etp_conf = self.__setting_files['system']
+7 -6
View File
@@ -1043,7 +1043,7 @@ class EntropyRepository(EntropyRepositoryPluginStore, TextInterface):
if manual_dep in pkgdata['dependencies']:
continue
pkgdata['dependencies'][manual_dep] = \
etpConst['spm']['mdepend_id']
etpConst['dependency_type_ids']['mdepend_id']
# FIXME: this is Entropy Client related but also part of the
# currently implemented metaphor, so let's wait to have a Rule
@@ -1261,7 +1261,8 @@ class EntropyRepository(EntropyRepositoryPluginStore, TextInterface):
for manual_dep in manual_deps:
if manual_dep in dep_dict:
continue
dep_dict[manual_dep] = etpConst['spm']['mdepend_id']
dep_dict[manual_dep] = \
etpConst['dependency_type_ids']['mdepend_id']
else:
# force to None
@@ -1937,7 +1938,7 @@ class EntropyRepository(EntropyRepositoryPluginStore, TextInterface):
"""
mydict = {}
for manual_dep in manual_deps:
mydict[manual_dep] = etpConst['spm']['mdepend_id']
mydict[manual_dep] = etpConst['dependency_type_ids']['mdepend_id']
return self.insertDependencies(idpackage, mydict)
def removeContent(self, idpackage):
@@ -4127,7 +4128,7 @@ class EntropyRepository(EntropyRepositoryPluginStore, TextInterface):
@type extended: bool
"""
return self.retrieveDependencies(idpackage, extended = extended,
deptype = etpConst['spm']['pdepend_id'])
deptype = etpConst['dependency_type_ids']['pdepend_id'])
def retrieveManualDependencies(self, idpackage, extended = False):
"""
@@ -4141,7 +4142,7 @@ class EntropyRepository(EntropyRepositoryPluginStore, TextInterface):
@type extended: bool
"""
return self.retrieveDependencies(idpackage, extended = extended,
deptype = etpConst['spm']['mdepend_id'])
deptype = etpConst['dependency_type_ids']['mdepend_id'])
def retrieveDependencies(self, idpackage, extended = False, deptype = None,
exclude_deptypes = None):
@@ -4154,7 +4155,7 @@ class EntropyRepository(EntropyRepositoryPluginStore, TextInterface):
composed by dependency name and dependency type)
@type extended: bool
@keyword deptype: return only given type of dependencies
see etpConst['spm']['*depend_id'] for dependency type
see etpConst['dependency_type_ids']['*depend_id'] for dependency type
identifiers
@type deptype: bool
@keyword exclude_deptypes: list of dependency types to exclude
+19 -19
View File
@@ -22,6 +22,7 @@ from entropy.i18n import _
from entropy.misc import RSS
from entropy.transceivers import EntropyTransceiver
from entropy.db import dbapi2
import entropy.tools
class Server:
@@ -1252,33 +1253,32 @@ class Server:
critical.append(ssl_server_cert)
# Some information regarding how packages are built
spm_files = [
(etpConst['spm']['global_make_conf'], "global_make_conf"),
(etpConst['spm']['global_package_keywords'],
"global_package_keywords"),
(etpConst['spm']['global_package_use'], "global_package_use"),
(etpConst['spm']['global_package_mask'], "global_package_mask"),
(etpConst['spm']['global_package_unmask'], "global_package_unmask"),
]
for myfile, myname in spm_files:
spm_files_map = self.Entropy.Spm_class().config_files_map()
spm_syms = {}
for myname, myfile in spm_files_map.items():
if os.path.islink(myfile):
spm_syms[myname] = myfile
continue # we don't want symlinks
if os.path.isfile(myfile) and os.access(myfile, os.R_OK):
data[myname] = myfile
extra_text_files.append(myfile)
make_profile = etpConst['spm']['global_make_profile']
rnd_tmp_file = self.Entropy.entropyTools.get_random_temp_file()
mytmpdir = os.path.dirname(rnd_tmp_file)
mytmpfile = os.path.join(mytmpdir,
etpConst['spm']['global_make_profile_link_name'])
extra_text_files.append(mytmpfile)
# XXX/FIXME: for symlinks, we read their link and send a file with that
# content. This is the default behaviour for now and allows to send
# /etc/make.profile link pointer correctly.
for symname, symfile in spm_syms.items():
if os.path.islink(make_profile):
mylink = os.readlink(make_profile)
tmp_file = entropy.tools.get_random_temp_file()
mytmpdir = os.path.dirname(tmp_file)
mytmpfile = os.path.join(mytmpdir, os.path.basename(symfile))
extra_text_files.append(mytmpfile)
mylink = os.readlink(symfile)
f_mkp = open(mytmpfile, "w")
f_mkp.write(mylink)
f_mkp.flush()
f_mkp.close()
data['global_make_profile'] = mytmpfile
data[symname] = mytmpfile
return data, critical, extra_text_files
@@ -3033,7 +3033,7 @@ class Server:
header = darkred(" !!! ")
)
exc_txt = self.Entropy.entropyTools.print_exception(
exc_txt = entropy.tools.print_exception(
returndata = True)
for line in exc_txt:
self.Entropy.updateProgress(
+72 -69
View File
@@ -155,87 +155,85 @@ class Base:
stdout_err.close()
return True, rc
def compile_atoms( self,
queue_id, atoms,
pretend = False, oneshot = False,
verbose = True, nocolor = True,
fetchonly = False, buildonly = False,
nodeps = False, custom_use = '', ldflags = '', cflags = ''
):
def compile_atoms(
self,
queue_id, atoms,
pretend = False, oneshot = False,
verbose = True, nocolor = True,
fetchonly = False, buildonly = False,
nodeps = False, custom_use = '', ldflags = '', cflags = ''):
queue_data, key = self.SystemManagerExecutor.SystemInterface.get_item_by_queue_id(queue_id, copy = True)
sys_intf = self.SystemManagerExecutor.SystemInterface
queue_data, key = sys_intf.get_item_by_queue_id(queue_id, copy = True)
if queue_data == None:
return False, 'no item in queue'
def set_proc_pid(pid):
self._set_processing_pid(queue_id, pid)
stdout_err = open(queue_data['stdout'], "a+")
cmd = [etpConst['spm']['env_update_cmd'], "&&"]
cmd += etpConst['spm']['source_profile']+["&&"]
if custom_use:
cmd += ['export USE="']+custom_use.strip().split()+['"', '&&']
if ldflags:
cmd += ['export LDFLAGS="']+custom_use.strip().split()+['"', '&&']
if cflags:
cmd += ['export CFLAGS="']+custom_use.strip().split()+['"', '&&']
cmd += [etpConst['spm']['exec']]+atoms
if pretend:
cmd.append(etpConst['spm']['pretend_cmd'])
if verbose:
cmd.append(etpConst['spm']['verbose_cmd'])
if oneshot:
cmd.append(etpConst['spm']['oneshot_cmd'])
if nocolor:
cmd.append(etpConst['spm']['nocolor_cmd'])
if fetchonly:
cmd.append(etpConst['spm']['fetchonly_cmd'])
if buildonly:
cmd.append(etpConst['spm']['buildonly_cmd'])
if nodeps:
cmd.append(etpConst['spm']['nodeps_cmd'])
stdout_err.write("Preparing to spawn parameter: '%s'. Good luck mate!\n" % (' '.join(cmd),))
stdout_err.write("Preparing to spawn compilation of: '%s'"
". Good luck mate!\n" % (' '.join(atoms),))
stdout_err.flush()
env = {}
if ldflags:
env['LDFLAGS'] = ldflags
if cflags:
env['CFLAGS'] = cflags
try:
p = subprocess.Popen(' '.join(cmd), stdout = stdout_err, stderr = stdout_err, stdin = self._get_stdin(queue_id), shell = True)
self._set_processing_pid(queue_id, p.pid)
rc = p.wait()
spm = sys_intf.Entropy.Spm()
status = spm.compile_packages(atoms,
stdin = self._get_stdin(queue_id),
stdout = stdour_err, stderr = stdout_err, environ = env,
pid_write_func = set_proc_pid, pretend = pretend,
verbose = verbose, fetch_only = fetchonly,
build_only = buildonly, no_dependencies = nodeps,
coloured_output = not nocolor, oneshot = oneshot)
finally:
stdout_err.write("\n### Done ###\n")
stdout_err.flush()
stdout_err.close()
return True, rc
return True, status
def spm_remove_atoms(self, queue_id, atoms, pretend = True, verbose = True, nocolor = True):
queue_data, key = self.SystemManagerExecutor.SystemInterface.get_item_by_queue_id(queue_id, copy = True)
sys_intf = self.SystemManagerExecutor.SystemInterface
queue_data, key = sys_intf.get_item_by_queue_id(queue_id, copy = True)
if queue_data == None:
return False, 'no item in queue'
def set_proc_pid(pid):
self._set_processing_pid(queue_id, pid)
stdout_err = open(queue_data['stdout'], "a+")
cmd = [etpConst['spm']['env_update_cmd'], "&&"]
cmd += etpConst['spm']['source_profile']+["&&"]
cmd += [etpConst['spm']['exec'], etpConst['spm']['remove_cmd']]+atoms
if pretend:
cmd.append(etpConst['spm']['pretend_cmd'])
if verbose:
cmd.append(etpConst['spm']['verbose_cmd'])
if nocolor:
cmd.append(etpConst['spm']['nocolor_cmd'])
stdout_err.write("Preparing to spawn parameter: '%s'. Good luck mate!\n" % (' '.join(cmd),))
stdout_err.write("Preparing to spawn removal of: '%s'"
". Good luck mate!\n" % (' '.join(atoms),))
stdout_err.flush()
try:
p = subprocess.Popen(' '.join(cmd), stdout = stdout_err, stderr = stdout_err, stdin = self._get_stdin(queue_id), shell = True)
self._set_processing_pid(queue_id, p.pid)
rc = p.wait()
spm = sys_intf.Entropy.Spm()
status = spm.remove_packages(atoms,
stdin = self._get_stdin(queue_id),
stdout = stdour_err, stderr = stdout_err,
pid_write_func = set_proc_pid, pretend = pretend,
verbose = verbose, fetch_only = fetchonly,
build_only = buildonly, no_dependencies = nodeps,
coloured_output = not nocolor, oneshot = oneshot)
finally:
stdout_err.write("\n### Done ###\n")
stdout_err.flush()
stdout_err.close()
return True, rc
return True, status
def enable_uses_for_atoms(self, queue_id, atoms, useflags):
@@ -350,26 +348,33 @@ class Base:
def run_spm_info(self, queue_id):
queue_data, key = self.SystemManagerExecutor.SystemInterface.get_item_by_queue_id(queue_id, copy = True)
sys_intf = self.SystemManagerExecutor.SystemInterface
queue_data, key = sys_intf.get_item_by_queue_id(queue_id, copy = True)
if queue_data == None:
return False, 'no item in queue'
def set_proc_pid(pid):
self._set_processing_pid(queue_id, pid)
stdout_err = open(queue_data['stdout'], "a+")
cmd = [etpConst['spm']['exec'], etpConst['spm']['info_cmd']]
stdout_err.write("Preparing to spawn parameter: '%s'. Good luck mate!\n" % (' '.join(cmd),))
stdout_err.write("Preparing to spawn SPM info. Good luck mate!\n")
stdout_err.flush()
try:
p = subprocess.Popen(cmd, stdout = stdout_err, stderr = stdout_err, stdin = self._get_stdin(queue_id))
self._set_processing_pid(queue_id, p.pid)
rc = p.wait()
spm = sys_intf.Entropy.Spm()
status = spm.print_build_environment_info(
stdin = self._get_stdin(queue_id),
stdout = stdour_err, stderr = stdout_err,
pid_write_func = set_proc_pid, coloured_output = False)
finally:
stdout_err.write("\n### Done ###\n")
stdout_err.flush()
stdout_err.close()
return True, rc
return True, status
def run_custom_shell_command(self, queue_id, command):
@@ -379,16 +384,14 @@ class Base:
stdout_err = open(queue_data['stdout'], "a+")
cmd = [etpConst['spm']['env_update_cmd'], "&&"]
cmd += etpConst['spm']['source_profile']+[";"]
cmd += command.split()
cmd = command.split()
cmd = ' '.join(cmd)
stdout_err.write("Preparing to spawn parameter: '%s'. Good luck mate!\n" % (cmd,))
stdout_err.flush()
try:
p = subprocess.Popen(cmd, stdout = stdout_err, stderr = stdout_err, stdin = self._get_stdin(queue_id), shell = True)
p = subprocess.Popen(cmd, stdout = stdout_err, stderr = stdout_err,
stdin = self._get_stdin(queue_id), shell = True)
self._set_processing_pid(queue_id, p.pid)
rc = p.wait()
finally:
+31 -6
View File
@@ -16,16 +16,41 @@ from entropy.i18n import _
from entropy.spm.plugins.skel import SpmPlugin
import entropy.spm.plugins.interfaces as plugs
settings = SystemSettings()
default_plugin = settings['system'].get('spm_backend',
etpConst['spm']['backend'])
_settings = SystemSettings()
_USER_PLUG = _settings['system'].get('spm_backend')
FACTORY = EntropyPluginFactory(SpmPlugin, plugs,
default_plugin_name = default_plugin,
fallback_plugin_name = etpConst['spm']['backend'])
default_plugin_name = _USER_PLUG)
get_available_plugins = FACTORY.get_available_plugins
get_default_class = FACTORY.get_default_plugin
def get_default_class():
"""
Return default Source Package Manager plugin class.
@return: default Source Package Manager plugin class
@raise SystemError: if no default plugin class has been specified.
This usually means a programming error.
"""
fallback_used = False
myplugs = get_available_plugins()
if _USER_PLUG is not None:
user_plugin = myplugs.get(_USER_PLUG)
if user_plugin is not None:
return user_plugin
fallback_used = True
for plug_id in sorted(myplugs):
plug_class = myplugs[plug_id]
if plug_class.IS_DEFAULT:
if fallback_used:
import warnings
warnings.warn("%s: %s" % (
"User configured Source Package Manager Plugin not available, "
"using fallback", plug_id,))
return plug_class
raise SystemError("no SPM default plugin configured")
def get_default_instance(output_interface):
"""
@@ -14,6 +14,7 @@ import bz2
import sys
import shutil
import tempfile
import subprocess
from entropy.const import etpConst, etpUi, const_get_stringtype, \
const_convert_to_unicode, const_convert_to_rawstring
from entropy.exceptions import FileNotFound, SPMError, InvalidDependString, \
@@ -155,7 +156,52 @@ class PortagePlugin(SpmPlugin):
"downgrade", "unavailable"
]
package_phases_map = {
xpak_entries = {
'description': "DESCRIPTION",
'homepage': "HOMEPAGE",
'chost': "CHOST",
'category': "CATEGORY",
'cflags': "CFLAGS",
'cxxflags': "CXXFLAGS",
'license': "LICENSE",
'src_uri': "SRC_URI",
'use': "USE",
'iuse': "IUSE",
'slot': "SLOT",
'provide': "PROVIDE",
'depend': "DEPEND",
'rdepend': "RDEPEND",
'pdepend': "PDEPEND",
'needed': "NEEDED",
'inherited': "INHERITED",
'keywords': "KEYWORDS",
'contents': "CONTENTS",
'counter': "COUNTER",
'defined_phases': "DEFINED_PHASES",
'pf': "PF",
}
_ebuild_entries = {
'ebuild_pkg_tag_var': "ENTROPY_PROJECT_TAG",
}
_cmd_map = {
'env_update_cmd': "/usr/sbin/env-update",
'ask_cmd': "--ask",
'info_cmd': "--info",
'remove_cmd': "-C",
'nodeps_cmd': "--nodeps",
'fetchonly_cmd': "--fetchonly",
'buildonly_cmd': "--buildonly",
'oneshot_cmd': "--oneshot",
'pretend_cmd': "--pretend",
'verbose_cmd': "--verbose",
'nocolor_cmd': "--color=n",
'source_profile_cmd': ["source", "/etc/profile"],
'exec_cmd': "/usr/bin/emerge",
}
_package_phases_map = {
'setup': 'setup',
'preinstall': 'preinst',
'postinstall': 'postinst',
@@ -164,7 +210,16 @@ class PortagePlugin(SpmPlugin):
'configure': 'config',
}
PLUGIN_API_VERSION = 2
_config_files_map = {
'global_make_conf': "/etc/make.conf",
'global_package_keywords': "/etc/portage/package.keywords",
'global_package_use': "/etc/portage/package.use",
'global_package_mask': "/etc/portage/package.mask",
'global_package_unmask': "/etc/portage/package.unmask",
'global_make_profile': "/etc/make.profile",
}
PLUGIN_API_VERSION = 3
SUPPORTED_MATCH_TYPES = [
"bestmatch-visible", "cp-list", "list-visible", "match-all",
@@ -178,6 +233,7 @@ class PortagePlugin(SpmPlugin):
'portagetree': {},
}
IS_DEFAULT = True
PLUGIN_NAME = 'portage'
ENV_FILE_COMP = "environment.bz2"
EBUILD_EXT = ".ebuild"
@@ -349,7 +405,6 @@ class PortagePlugin(SpmPlugin):
for package in self.portage.settings.packages:
pkgs = self.match_installed_package(package, match_all = True)
system.extend(pkgs)
system.extend(etpConst['spm']['system_packages'])
return system
def get_package_categories(self):
@@ -768,12 +823,12 @@ class PortagePlugin(SpmPlugin):
data['keywords'] = set(data['keywords'])
needed_file = os.path.join(meta_dir,
etpConst['spm']['xpak_entries']['needed'])
PortagePlugin.xpak_entries['needed'])
data['needed'] = self._extract_pkg_metadata_needed(needed_file)
content_file = os.path.join(meta_dir,
etpConst['spm']['xpak_entries']['contents'])
PortagePlugin.xpak_entries['contents'])
data['content'] = self._extract_pkg_metadata_content(content_file,
package_file)
data['disksize'] = entropy.tools.sum_file_sizes(data['content'])
@@ -788,9 +843,9 @@ class PortagePlugin(SpmPlugin):
if data['category'] != PortagePlugin.KERNEL_CATEGORY:
kern_dep_key = self._add_kernel_dependency_to_pkg(data)
file_ext = etpConst['spm']['ebuild_file_extension']
file_ext = PortagePlugin.EBUILD_EXT
ebuilds_in_path = [x for x in os.listdir(meta_dir) if \
x.endswith(".%s" % (file_ext,))]
x.endswith(file_ext)]
if not data['versiontag'] and ebuilds_in_path:
# has the user specified a custom package tag inside the ebuild
@@ -853,12 +908,14 @@ class PortagePlugin(SpmPlugin):
for x in portage_metadata['RDEPEND'].split():
if x.startswith("!") or (x in ("(", "||", ")", "")):
continue
data['dependencies'][x] = etpConst['spm']['(r)depend_id']
data['dependencies'][x] = \
etpConst['dependency_type_ids']['(r)depend_id']
for x in portage_metadata['PDEPEND'].split():
if x.startswith("!") or (x in ("(", "||", ")", "")):
continue
data['dependencies'][x] = etpConst['spm']['pdepend_id']
data['dependencies'][x] = \
etpConst['dependency_type_ids']['pdepend_id']
data['conflicts'] = [x.replace("!", "") for x in \
portage_metadata['RDEPEND'].split() + \
@@ -866,7 +923,8 @@ class PortagePlugin(SpmPlugin):
x.startswith("!") and not x in ("(", "||", ")", "")]
if kern_dep_key is not None:
data['dependencies'][kern_dep_key] = etpConst['spm']['(r)depend_id']
data['dependencies'][kern_dep_key] = \
etpConst['dependency_type_ids']['(r)depend_id']
# Conflicting tagged packages support
# Needs Entropy Client System Settings Plugin,
@@ -1172,6 +1230,119 @@ class PortagePlugin(SpmPlugin):
return available
def compile_packages(self, packages, stdin = None, stdout = None,
stderr = None, environ = None, pid_write_func = None,
pretend = False, verbose = False, fetch_only = False,
build_only = False, no_dependencies = False,
ask = False, coloured_output = False, oneshot = False):
cmd = [PortagePlugin._cmd_map['exec_cmd']]
if pretend:
cmd.append(PortagePlugin._cmd_map['pretend_cmd'])
if verbose:
cmd.append(PortagePlugin._cmd_map['verbose_cmd'])
if ask:
cmd.append(PortagePlugin._cmd_map['ask_cmd'])
if oneshot:
cmd.append(PortagePlugin._cmd_map['oneshot_cmd'])
if not coloured_output:
cmd.append(PortagePlugin._cmd_map['nocolor_cmd'])
if fetch_only:
cmd.append(PortagePlugin._cmd_map['fetchonly_cmd'])
if build_only:
cmd.append(PortagePlugin._cmd_map['buildonly_cmd'])
if no_dependencies:
cmd.append(PortagePlugin._cmd_map['nodeps_cmd'])
cmd.extend(packages)
cmd_string = """\
%s && %s && %s
""" % (PortagePlugin._cmd_map['env_update_cmd'],
PortagePlugin._cmd_map['source_profile_cmd'],
' '.join(cmd)
)
env = os.environ.copy()
if environ is not None:
env.update(environ)
proc = subprocess.Popen(cmd_string, stdout = stdout, stderr = stderr,
stdin = stdin, env = env, shell = True)
if pid_write_func is not None:
pid_write_func(proc.pid)
return proc.wait()
def remove_packages(self, packages, stdin = None, stdout = None,
stderr = None, environ = None, pid_write_func = None,
pretend = False, verbose = False, no_dependencies = False, ask = False,
coloured_output = False):
cmd = [PortagePlugin._cmd_map['exec_cmd'],
PortagePlugin._cmd_map['remove_cmd']]
if pretend:
cmd.append(PortagePlugin._cmd_map['pretend_cmd'])
if verbose:
cmd.append(PortagePlugin._cmd_map['verbose_cmd'])
if ask:
cmd.append(PortagePlugin._cmd_map['ask_cmd'])
if not coloured_output:
cmd.append(PortagePlugin._cmd_map['nocolor_cmd'])
if no_dependencies:
cmd.append(PortagePlugin._cmd_map['nodeps_cmd'])
cmd.extend(packages)
cmd_string = """\
%s && %s && %s
""" % (PortagePlugin._cmd_map['env_update_cmd'],
PortagePlugin._cmd_map['source_profile_cmd'],
' '.join(cmd)
)
env = os.environ.copy()
if environ is not None:
env.update(environ)
proc = subprocess.Popen(cmd_string, stdout = stdout, stderr = stderr,
stdin = stdin, env = env, shell = True)
if pid_write_func is not None:
pid_write_func(proc.pid)
return proc.wait()
def environment_update(self, stdout = None, stderr = None):
kwargs = {}
if etpUi['mute']:
kwargs['stdout'] = stdout
kwargs['stderr'] = stderr
args = (PortagePlugin._cmd_map['env_update_cmd'],)
proc = subprocess.Popen(args, **kwargs)
proc.wait()
def print_build_environment_info(self, stdin = None, stdout = None,
stderr = None, environ = None, pid_write_func = None,
coloured_output = False):
cmd = [PortagePlugin._cmd_map['exec_cmd'],
PortagePlugin._cmd_map['info_cmd']]
if not coloured_output:
cmd.append(PortagePlugin._cmd_map['nocolor_cmd'])
cmd_string = """\
%s && %s && %s
""" % (PortagePlugin._cmd_map['env_update_cmd'],
PortagePlugin._cmd_map['source_profile_cmd'],
' '.join(cmd)
)
env = os.environ.copy()
if environ is not None:
env.update(environ)
proc = subprocess.Popen(cmd_string, stdout = stdout, stderr = stderr,
stdin = stdin, env = env, shell = True)
if pid_write_func is not None:
pid_write_func(proc.pid)
return proc.wait()
def get_installed_packages(self, categories = None, root = None):
"""
Reimplemented from SpmPlugin class.
@@ -1225,7 +1396,7 @@ class PortagePlugin(SpmPlugin):
root = root)
counter_dir = os.path.dirname(dbbuild)
counter_name = etpConst['spm']['xpak_entries']['counter']
counter_name = PortagePlugin.xpak_entries['counter']
counter_path = os.path.join(counter_dir, counter_name)
if not os.access(counter_dir, os.W_OK):
@@ -1243,7 +1414,7 @@ class PortagePlugin(SpmPlugin):
"""
Reimplemented from SpmPlugin class.
"""
counter_path = etpConst['spm']['xpak_entries']['counter']
counter_path = PortagePlugin.xpak_entries['counter']
entropy_atom = entropy_repository.retrieveAtom(
entropy_repository_package_id)
@@ -2079,11 +2250,25 @@ class PortagePlugin(SpmPlugin):
entropy_repository_id, portage_dirs_digest)
entropy_repository.commitChanges()
@staticmethod
def package_phases_map():
"""
Reimplemented from SpmPlugin class.
"""
return PortagePlugin._package_phases_map.copy()
@staticmethod
def config_files_map():
"""
Reimplemented from SpmPlugin class.
"""
return PortagePlugin._config_files_map.copy()
def execute_package_phase(self, package_metadata, phase_name):
"""
Reimplemented from SpmPlugin class.
"""
portage_phase = PortagePlugin.package_phases_map[phase_name]
portage_phase = PortagePlugin._package_phases_map[phase_name]
phase_calls = {
'setup': self._pkg_setup,
'preinst': self._pkg_preinst,
@@ -2396,6 +2581,207 @@ class PortagePlugin(SpmPlugin):
return env_rc, msg
@staticmethod
def _config_updates_make_conf(entropy_client, repo):
## WARNING: it doesn't handle multi-line variables, yet. remember this.
system_make_conf = PortagePlugin._config_files_map['global_make_conf']
avail_data = entropy_client.SystemSettings['repositories']['available']
repo_dbpath = avail_data[repo]['dbpath']
repo_make_conf = os.path.join(repo_dbpath,
os.path.basename(system_make_conf))
if not (os.path.isfile(repo_make_conf) and \
os.access(repo_make_conf, os.R_OK)):
return
make_conf_variables_check = ["CHOST"]
if not os.path.isfile(system_make_conf):
entropy_client.updateProgress(
"%s %s. %s." % (
red(system_make_conf),
blue(_("does not exist")), blue(_("Overwriting")),
),
importance = 1,
type = "info",
header = blue(" @@ ")
)
if os.path.lexists(system_make_conf):
shutil.move(
system_make_conf,
"%s.backup_%s" % (system_make_conf,
entropy.tools.get_random_number(),)
)
shutil.copy2(repo_make_conf, system_make_conf)
elif os.access(system_make_conf, os.W_OK):
repo_f = open(repo_make_conf, "r")
sys_f = open(system_make_conf, "r")
repo_make_c = [x.strip() for x in repo_f.readlines()]
sys_make_c = [x.strip() for x in sys_f.readlines()]
repo_f.close()
sys_f.close()
# read repository settings
repo_data = {}
for setting in make_conf_variables_check:
for line in repo_make_c:
if line.startswith(setting+"="):
# there can't be bash vars with a space
# after its name on declaration
repo_data[setting] = line
# I don't break, because there might be
# other overlapping settings
differences = {}
# update make.conf data in memory
for setting in repo_data:
for idx in range(len(sys_make_c)):
line = sys_make_c[idx]
if line.startswith(setting+"=") and \
(line != repo_data[setting]):
# there can't be bash vars with a
# space after its name on declaration
entropy_client.updateProgress(
"%s: %s %s. %s." % (
red(system_make_conf), bold(repr(setting)),
blue(_("variable differs")), red(_("Updating")),
),
importance = 1,
type = "info",
header = blue(" @@ ")
)
differences[setting] = repo_data[setting]
line = repo_data[setting]
sys_make_c[idx] = line
if differences:
entropy_client.updateProgress(
"%s: %s." % (
red(system_make_conf),
blue(_("updating critical variables")),
),
importance = 1,
type = "info",
header = blue(" @@ ")
)
# backup user make.conf
shutil.copy2(system_make_conf,
"%s.entropy_backup" % (system_make_conf,))
entropy_client.updateProgress(
"%s: %s." % (
red(system_make_conf),
darkgreen("writing changes to disk"),
),
importance = 1,
type = "info",
header = blue(" @@ ")
)
# write to disk, safely
tmp_make_conf = "%s.entropy_write" % (system_make_conf,)
f = open(tmp_make_conf, "w")
for line in sys_make_c:
f.write(line+"\n")
f.flush()
f.close()
shutil.move(tmp_make_conf, system_make_conf)
# update environment
for var in differences:
try:
myval = '='.join(differences[var].strip().split("=")[1:])
if myval:
if myval[0] in ("'", '"',): myval = myval[1:]
if myval[-1] in ("'", '"',): myval = myval[:-1]
except IndexError:
myval = ''
os.environ[var] = myval
@staticmethod
def _config_updates_make_profile(entropy_client, repo):
avail_data = entropy_client.SystemSettings['repositories']['available']
repo_dbpath = avail_data[repo]['dbpath']
profile_link = PortagePlugin._config_files_map['global_make_profile']
profile_link_name = os.path.basename(profile_link)
repo_make_profile = os.path.join(repo_dbpath, profile_link_name)
if not (os.path.isfile(repo_make_profile) and \
os.access(repo_make_profile, os.R_OK)):
return
system_make_profile = \
PortagePlugin._config_files_map['global_make_profile']
f = open(repo_make_profile, "r")
repo_profile_link_data = f.readline().strip()
f.close()
current_profile_link = ''
if os.path.islink(system_make_profile) and \
os.access(system_make_profile, os.R_OK):
current_profile_link = os.readlink(system_make_profile)
if (repo_profile_link_data != current_profile_link) and \
repo_profile_link_data:
entropy_client.updateProgress(
"%s: %s %s. %s." % (
red(system_make_profile), blue("link"),
blue(_("differs")), red(_("Updating")),
),
importance = 1,
type = "info",
header = blue(" @@ ")
)
merge_sfx = ".entropy_merge"
os.symlink(repo_profile_link_data, system_make_profile+merge_sfx)
if entropy.tools.is_valid_path(system_make_profile+merge_sfx):
os.rename(system_make_profile+merge_sfx, system_make_profile)
else:
# revert change, link does not exist yet
entropy_client.updateProgress(
"%s: %s %s. %s." % (
red(system_make_profile), blue("new link"),
blue(_("does not exist")), red(_("Reverting")),
),
importance = 1,
type = "info",
header = blue(" @@ ")
)
os.remove(system_make_profile+merge_sfx)
@staticmethod
def entropy_client_post_repository_update_hook(entropy_client,
entropy_repository_id):
# are we root?
if etpConst['uid'] != 0:
entropy_client.updateProgress(
brown(_("Skipping configuration files update, you are not root.")),
importance = 1,
type = "info",
header = blue(" @@ ")
)
return 0
default_repo = \
entropy_client.SystemSettings['repositories']['default_repository']
if default_repo == entropy_repository_id:
PortagePlugin._config_updates_make_conf(entropy_repository_id)
PortagePlugin._config_updates_make_profile(entropy_repository_id)
return 0
@staticmethod
def entropy_install_setup_hook(entropy_client, package_metadata):
"""
@@ -3280,83 +3666,83 @@ class PortagePlugin(SpmPlugin):
def _extract_pkg_metadata_generate_extraction_dict(self):
data = {
'pf': {
'path': etpConst['spm']['xpak_entries']['pf'],
'path': PortagePlugin.xpak_entries['pf'],
'critical': True,
},
'chost': {
'path': etpConst['spm']['xpak_entries']['chost'],
'path': PortagePlugin.xpak_entries['chost'],
'critical': True,
},
'description': {
'path': etpConst['spm']['xpak_entries']['description'],
'path': PortagePlugin.xpak_entries['description'],
'critical': False,
},
'homepage': {
'path': etpConst['spm']['xpak_entries']['homepage'],
'path': PortagePlugin.xpak_entries['homepage'],
'critical': False,
},
'slot': {
'path': etpConst['spm']['xpak_entries']['slot'],
'path': PortagePlugin.xpak_entries['slot'],
'critical': False,
},
'cflags': {
'path': etpConst['spm']['xpak_entries']['cflags'],
'path': PortagePlugin.xpak_entries['cflags'],
'critical': False,
},
'cxxflags': {
'path': etpConst['spm']['xpak_entries']['cxxflags'],
'path': PortagePlugin.xpak_entries['cxxflags'],
'critical': False,
},
'category': {
'path': etpConst['spm']['xpak_entries']['category'],
'path': PortagePlugin.xpak_entries['category'],
'critical': True,
},
'rdepend': {
'path': etpConst['spm']['xpak_entries']['rdepend'],
'path': PortagePlugin.xpak_entries['rdepend'],
'critical': False,
},
'pdepend': {
'path': etpConst['spm']['xpak_entries']['pdepend'],
'path': PortagePlugin.xpak_entries['pdepend'],
'critical': False,
},
'depend': {
'path': etpConst['spm']['xpak_entries']['depend'],
'path': PortagePlugin.xpak_entries['depend'],
'critical': False,
},
'use': {
'path': etpConst['spm']['xpak_entries']['use'],
'path': PortagePlugin.xpak_entries['use'],
'critical': False,
},
'iuse': {
'path': etpConst['spm']['xpak_entries']['iuse'],
'path': PortagePlugin.xpak_entries['iuse'],
'critical': False,
},
'license': {
'path': etpConst['spm']['xpak_entries']['license'],
'path': PortagePlugin.xpak_entries['license'],
'critical': False,
},
'provide': {
'path': etpConst['spm']['xpak_entries']['provide'],
'path': PortagePlugin.xpak_entries['provide'],
'critical': False,
},
'sources': {
'path': etpConst['spm']['xpak_entries']['src_uri'],
'path': PortagePlugin.xpak_entries['src_uri'],
'critical': False,
},
'eclasses': {
'path': etpConst['spm']['xpak_entries']['inherited'],
'path': PortagePlugin.xpak_entries['inherited'],
'critical': False,
},
'counter': {
'path': etpConst['spm']['xpak_entries']['counter'],
'path': PortagePlugin.xpak_entries['counter'],
'critical': False,
},
'keywords': {
'path': etpConst['spm']['xpak_entries']['keywords'],
'path': PortagePlugin.xpak_entries['keywords'],
'critical': False,
},
'spm_phases': {
'path': etpConst['spm']['xpak_entries']['defined_phases'],
'path': PortagePlugin.xpak_entries['defined_phases'],
'critical': False,
},
}
@@ -3567,7 +3953,7 @@ class PortagePlugin(SpmPlugin):
return pkg_links
def _extract_pkg_metadata_ebuild_entropy_tag(self, ebuild):
search_tag = etpConst['spm']['ebuild_pkg_tag_var']
search_tag = PortagePlugin._ebuild_entries['ebuild_pkg_tag_var']
ebuild_tag = ''
# open in unicode fmt
f = open(ebuild, "r")
+176 -4
View File
@@ -18,7 +18,7 @@ from entropy.misc import LogFile
class SpmPlugin(Singleton):
"""Base class for Source Package Manager plugins"""
BASE_PLUGIN_API_VERSION = 2
BASE_PLUGIN_API_VERSION = 3
# this must be reimplemented by subclasses and value
# must match BASE_PLUGIN_API_VERSION
@@ -31,12 +31,16 @@ class SpmPlugin(Singleton):
# Name of your Spm Plugin
PLUGIN_NAME = None
# At least one of your SpmPlugin classes must be set as default
# by setting IS_DEFAULT = True
# There can't be more than _one_ default SPM plugin, in that case
# the first (alphabetically) one will be automatically selected
IS_DEFAULT = False
def init_singleton(self, output_interface):
"""
Source Package Manager Plugin singleton method.
This method must be reimplemented by subclasses.
At this stage, you should also consider to tweak etpConst['spm']
content (importing etpConst from entropy.const).
@param output_interface: Entropy output interface
@type output_interface: entropy.output.TextInterface based instances
@@ -61,7 +65,8 @@ class SpmPlugin(Singleton):
"""
raise NotImplementedError()
def package_phases(self):
@staticmethod
def package_phases():
"""
Return a list of available and valid package build phases.
Default value is ["setup", "preinstall", "postinstall", "preremove",
@@ -73,6 +78,29 @@ class SpmPlugin(Singleton):
return ["setup", "preinstall", "postinstall", "preremove",
"postremove", "configure"]
@staticmethod
def package_phases_map():
"""
Return a map of phases names between Entropy (as keys) and
Source Package Manager.
@return: map of package phases
@rtype: dict
"""
raise NotImplementedError()
@staticmethod
def config_files_map():
"""
Return a map composed by configuration file identifiers and their
path on disk. These configuration files are related to Source Package
Manager.
@return: configuration files map
@rtype: dict
"""
raise NotImplementedError()
def get_cache_directory(self, root = None):
"""
Return Source Package Manager cache directory path.
@@ -441,6 +469,133 @@ class SpmPlugin(Singleton):
"""
raise NotImplementedError()
def compile_packages(self, packages, stdin = None, stdout = None,
stderr = None, environ = None, pid_write_func = None,
pretend = False, verbose = False, fetch_only = False,
build_only = False, no_dependencies = False,
ask = False, coloured_output = False, oneshot = False):
"""
Compile given packages using given compile options. Extra compiler
options can be set via environmental variables (CFLAGS, LDFLAGS, etc).
By default, this function writes to stdout and can potentially interact
with user via stdin/stdout/stderr.
By default, when build_only=False, compiled packages are installed onto
the running system.
@param packages: list of Source Package Manager package names
@type packages: list
@keyword stdin: custom standard input
@type stdin: file object or valid file descriptor number
@keyword stdout: custom standard output
@type stdout: file object or valid file descriptor number
@keyword stderr: custom standard error
@type stderr: file object or valid file descriptor number
@keyword environ: dict
@type environ: map of environmental variables
@keyword pid_write_func: function to call with execution pid number
@type pid_write_func: callable function, signature func(int_pid_number)
@keyword pretend: just show what would be done
@type pretend: bool
@keyword verbose: execute compilation in verbose mode
@type verbose: bool
@keyword fetch_only: fetch source code only
@type fetch_only: bool
@keyword build_only: do not actually touch live system (don't install
compiled
@type build_only: bool
@keyword no_dependencies: ignore possible build time dependencies
@type no_dependencies: bool
@keyword ask: ask user via stdin before executing the required tasks
@type ask: bool
@keyword coloured_output: allow coloured output
@type coloured_output: bool
@keyword oneshot: when compiled packages are intended to not get
recorded into personal user compile preferences (if you are not
using a Portage-based SPM, just ignore this)
@type oneshot: bool
@return: execution status
@rtype: int
"""
raise NotImplementedError()
def remove_packages(self, packages, stdin = None, stdout = None,
stderr = None, environ = None, pid_write_func = None,
pretend = False, verbose = False, no_dependencies = False, ask = False,
coloured_output = False):
"""
Compile given packages using given compile options. Extra compiler
options can be set via environmental variables (CFLAGS, LDFLAGS, etc).
By default, this function writes to stdout and can potentially interact
with user via stdin/stdout/stderr.
By default, when build_only=False, compiled packages are installed onto
the running system.
@param packages: list of Source Package Manager package names
@type packages: list
@keyword stdin: custom standard input
@type stdin: file object or valid file descriptor number
@keyword stdout: custom standard output
@type stdout: file object or valid file descriptor number
@keyword stderr: custom standard error
@type stderr: file object or valid file descriptor number
@keyword environ: dict
@type environ: map of environmental variables
@keyword pid_write_func: function to call with execution pid number
@type pid_write_func: callable function, signature func(int_pid_number)
@keyword pretend: just show what would be done
@type pretend: bool
@keyword verbose: execute compilation in verbose mode
@type verbose: bool
@keyword no_dependencies: ignore possible build time dependencies
@type no_dependencies: bool
@keyword ask: ask user via stdin before executing the required tasks
@type ask: bool
@keyword coloured_output: allow coloured output
@type coloured_output: bool
@return: execution status
@rtype: int
"""
raise NotImplementedError()
def print_build_environment_info(self, stdin = None, stdout = None,
stderr = None, environ = None, pid_write_func = None,
coloured_output = False):
"""
Print build environment info to stdout.
@keyword stdin: custom standard input
@type stdin: file object or valid file descriptor number
@keyword stdout: custom standard output
@type stdout: file object or valid file descriptor number
@keyword stderr: custom standard error
@type stderr: file object or valid file descriptor number
@keyword environ: dict
@type environ: map of environmental variables
@keyword pid_write_func: function to call with execution pid number
@type pid_write_func: callable function, signature func(int_pid_number)
@keyword coloured_output: allow coloured output
@type coloured_output: bool
@return: execution status
@rtype: int
"""
raise NotImplementedError()
def environment_update(self, stdout = None, stderr = None):
"""
Hook used by Entropy Client and Entropy Server to ask Source Package
Manager to update /etc/profile* and other environment settings around.
Since this is part of the Source Package Manager metaphor it must stay
in this class.
@keyword stdout: custom standard output
@type stdout: file object or valid file descriptor number
@keyword stderr: custom standard error
@type stderr: file object or valid file descriptor number
@return: execution status
@rtype: int
"""
raise NotImplementedError()
def get_installed_packages(self, categories = None, root = None):
"""
Return list of packages found in installed packages repository.
@@ -645,6 +800,23 @@ class SpmPlugin(Singleton):
"""
raise NotImplementedError()
@staticmethod
def entropy_client_post_repository_update_hook(entropy_client,
entropy_repository_id):
"""
This function is called by Entropy Client when updating Entropy
repositories. Place here all your Source Package Manager bullshit and,
remember to return an int form execution status.
@param entropy_client: Entropy Client interface instance
@type entropy_client: entropy.client.interfaces.Client.Client
@param entropy_repository_id: Entropy Repository unique identifier
@type: string
@return: execution status
@rtype: int
"""
raise NotImplementedError()
@staticmethod
def entropy_install_setup_hook(entropy_client, package_metadata):
"""
+4 -8
View File
@@ -20,6 +20,7 @@ import os
import time
import shutil
import tarfile
import tempfile
import subprocess
import grp
import pwd
@@ -2710,14 +2711,9 @@ def get_random_temp_file():
@return:
@rtype:
"""
if not os.path.isdir(etpConst['packagestmpdir']):
os.makedirs(etpConst['packagestmpdir'])
path = os.path.join(etpConst['packagestmpdir'],
"temp_"+str(get_random_number()))
while os.path.lexists(path):
path = os.path.join(etpConst['packagestmpdir'],
"temp_"+str(get_random_number()))
return path
fd, tmp_path = tempfile.mkstemp()
os.close(fd)
return tmp_path
def get_file_timestamp(path):
"""
+15 -21
View File
@@ -170,7 +170,8 @@ def repositories(options):
for idpackage in idpackages:
atom = dbconn.retrieveAtom(idpackage)
orig_deps = dbconn.retrieveDependencies(idpackage, extended = True)
atom_deps = [x for x in orig_deps if x[1] != etpConst['spm']['mdepend_id']]
atom_deps = [x for x in orig_deps if x[1] != \
etpConst['dependency_type_ids']['mdepend_id']]
atom_manual_deps = [x for x in orig_deps if x not in atom_deps]
print_info(brown(" @@ ")+"%s: %s:" % (blue(atom), darkgreen(_("package dependencies")),))
for dep_str, dep_id in atom_deps:
@@ -196,7 +197,8 @@ def repositories(options):
continue
w_dbconn = Entropy.open_server_repository(repo = repo, read_only = False)
atom_deps += [(x, etpConst['spm']['mdepend_id'],) for x in new_mdeps]
atom_deps += [(x, etpConst['dependency_type_ids']['mdepend_id'],) \
for x in new_mdeps]
deps_dict = {}
for atom_dep, dep_id in atom_deps:
deps_dict[atom_dep] = dep_id
@@ -762,15 +764,16 @@ def spm_compile_categories(options, do_list = False):
except ValueError:
break
spm = Entropy.Spm()
categories = sorted(set(options))
packages = Entropy.Spm().get_packages(categories)
packages = spm.get_packages(categories)
packages = sorted(packages)
# remove older packages from list (through slot)
if not oldslots:
oldslots_meta = {}
for package in packages:
pkg_slot = Entropy.Spm().get_package_metadata(package, "SLOT")
pkg_slot = spm.get_package_metadata(package, "SLOT")
pkg_key = entropy.tools.dep_getkey(package)
obj = oldslots_meta.setdefault(pkg_key, set())
obj.add((pkg_slot, package,))
@@ -782,10 +785,8 @@ def spm_compile_categories(options, do_list = False):
if do_list:
print_generic(' '.join(["="+x for x in packages]))
else:
args = [etpConst['spm']['exec'], etpConst['spm']['ask_cmd'],
etpConst['spm']['verbose_cmd']]
args.extend(["="+x for x in packages])
return subprocess.call(args)
return spm.compile_atoms(["="+x for x in packages],
ask = True, verbose = True)
return 0
def spm_compile_pkgset(pkgsets, do_rebuild = False, do_dbupdate = False,
@@ -804,33 +805,26 @@ def spm_compile_pkgset(pkgsets, do_rebuild = False, do_dbupdate = False,
_("package set not found"), pkgset,) ))
return 1
extra_args = []
if etpUi['ask']:
extra_args.append(etpConst['spm']['ask_cmd'])
if etpUi['verbose']:
extra_args.append(etpConst['spm']['verbose_cmd'])
if etpUi['pretend']:
extra_args.append(etpConst['spm']['pretend_cmd'])
spm = Entropy.Spm()
done_atoms = set()
# expand package sets
for pkgset in pkgsets:
set_atoms = [Entropy.Spm().match_package(x) for x in avail_sets[pkgset]]
set_atoms = [spm.match_package(x) for x in avail_sets[pkgset]]
set_atoms = [x for x in set_atoms if x]
if not do_rebuild:
set_atoms = [x for x in set_atoms if not \
Entropy.Spm().match_installed_package(x)]
spm.match_installed_package(x)]
set_atoms = ["="+x for x in set_atoms]
if not set_atoms:
continue
args = [etpConst['spm']['exec']]
args.extend(extra_args)
args.extend(set_atoms)
rc = subprocess.call(args)
rc = spm.compile_packages(set_atoms, verbose = etpUi['verbose'],
ask = etpUi['ask'], pretend = etpUi['pretend'])
if rc != 0:
return rc
done_atoms.update(set_atoms)