# -*- coding: utf-8 -*- """ @author: Fabio Erculiani @contact: lxnay@sabayon.org @copyright: Fabio Erculiani @license: GPL-2 B{Entropy Package Manager Client}. """ import os import sys from entropy.const import const_is_python3 if const_is_python3(): from subprocess import getoutput else: from commands import getoutput import shutil import tempfile from entropy.const import etpConst, etpSys, etpUi, const_setup_directory from entropy.output import red, darkred, darkgreen, brown, bold, \ print_info, print_error, print_warning from entropy.i18n import _ import entropy.dep import entropy.tools def smart(options): # check if I am root if not entropy.tools.is_root(): mytxt = _("You are not") # you are not root print_error(red(mytxt)+" "+bold("root")+red(".")) return 1 # Options available for all the packages submodules smart_req_savedir = None savedir = False newopts = [] for opt in options: if opt == "--savedir": savedir = True elif opt.startswith("--"): print_error(red(" %s." % (_("Wrong parameters"),) )) return else: if savedir: try: smart_req_savedir = os.path.realpath(opt) except OSError: smart_req_savedir = None savedir = False else: newopts.append(opt) options = newopts if not options: return -10 rc = 0 from entropy.client.interfaces import Client entropy_client = None acquired = False try: entropy_client = Client() acquired = entropy.tools.acquire_entropy_locks(entropy_client) if not acquired: print_error(darkgreen( _("Another Entropy is currently running."))) return 1 if options[0] == "package": rc = smart_pkg_handler(entropy_client, options[1:]) elif options[0] == "quickpkg": rc = quickpkg_handler(entropy_client, options[1:], savedir = smart_req_savedir) elif (options[0] == "inflate") or (options[0] == "deflate") or \ (options[0] == "extract"): rc = common_flate(entropy_client, options[1:], action = options[0], savedir = smart_req_savedir) else: rc = -10 finally: if acquired and (entropy_client is not None): entropy.tools.release_entropy_locks(entropy_client) if entropy_client is not None: entropy_client.shutdown() return rc def quickpkg_handler(entropy_client, mypackages, savedir = None): if (not mypackages): mytxt = _("No packages specified") if not etpUi['quiet']: print_error(darkred(" * ")+red(mytxt+".")) return 1 if savedir is None: savedir = etpConst['entropyunpackdir'] const_setup_directory(savedir) else: if not os.path.isdir(savedir): print_error(darkred(" * ")+red("--savedir ") + \ savedir+red(" %s." % (_("does not exist"),) )) return 4 packages = [] for opt in mypackages: match = entropy_client.installed_repository().atomMatch(opt) if match[0] != -1: packages.append(match) else: if not etpUi['quiet']: print_warning(darkred(" * ") + \ red("%s: " % (_("Cannot find"),))+bold(opt)) pkgs = [] for pkg in packages: if pkg not in pkgs: pkgs.append(pkg) packages = pkgs if not packages: print_error(darkred(" * ")+red("%s." % (_("No valid packages specified"),))) return 2 # print the list mytxt = _("This is the list of the packages that would be quickpkg'd") if (not etpUi['quiet']) or (etpUi['ask']): print_info(darkgreen(" * ")+red(mytxt+":")) pkgInfo = {} for pkg in packages: atom = entropy_client.installed_repository().retrieveAtom(pkg[0]) pkgInfo[pkg] = {} pkgInfo[pkg]['atom'] = atom pkgInfo[pkg]['idpackage'] = pkg[0] print_info(brown("\t[")+red("%s:" % (_("from"),))+bold(_("installed"))+brown("]")+" - "+atom) if (not etpUi['quiet']) or (etpUi['ask']): rc = entropy_client.ask_question(">> %s" % (_("Would you like to recompose the selected packages ?"),)) if rc == _("No"): return 0 for pkg in packages: if not etpUi['quiet']: print_info(brown(" * ")+red("%s: " % (_("Compressing"),))+darkgreen(pkgInfo[pkg]['atom'])) pkgdata = entropy_client.installed_repository().getPackageData(pkgInfo[pkg]['idpackage']) resultfile = entropy_client.generate_package(pkgdata, save_directory = savedir) if resultfile == None: if not etpUi['quiet']: print_error(darkred(" * ") + red("%s: " % (_("Error while creating package for"),)) + \ bold(pkgInfo[pkg])+darkred(". %s." % (_("Cannot continue"),))) return 3 if not etpUi['quiet']: print_info(darkgreen(" * ")+red("%s: " % (_("Saved in"),))+resultfile) return 0 def common_flate(entropy_client, mytbz2s, action, savedir = None): if (not mytbz2s): print_error(darkred(" * ")+red("%s." % (_("No packages specified"),))) return 1 # test if portage is available try: Spm = entropy_client.Spm() del Spm except Exception as e: entropy.tools.print_traceback() mytxt = _("Source Package Manager backend not available") print_error(darkred(" * ")+red("%s: %s" % (mytxt, e,))) return 1 if savedir: if not os.path.isdir(savedir): print_error(darkred(" * ") + bold("--savedir") + ":" \ + red(" %s." % (_("directory does not exist"),))) return 1 else: savedir = etpConst['entropyunpackdir'] const_setup_directory(savedir) for tbz2 in mytbz2s: valid_etp = True if action == "deflate": valid_etp = entropy.tools.is_entropy_package_file(tbz2) if not (os.path.isfile(tbz2) and \ tbz2.endswith(etpConst['packagesext']) and \ valid_etp): print_error( darkred(" * ") + bold(tbz2) + \ red(" %s" % (_("is not a valid Entropy package"),))) return 1 if action == "inflate": rc = inflate_handler(entropy_client, mytbz2s, savedir) elif action == "deflate": rc = deflate_handler(mytbz2s, savedir) elif action == "extract": rc = extract_handler(mytbz2s, savedir) else: rc = -10 return rc def inflate_handler(entropy_client, mytbz2s, savedir): branch = entropy_client.Settings()['repositories']['branch'] print_info(brown(" %s: " % (_("Using branch"),))+bold(branch)) Spm = entropy_client.Spm() # analyze files for tbz2 in mytbz2s: print_info(darkgreen(" * ")+darkred("Inflating: ")+tbz2, back = True) etptbz2path = savedir+os.path.sep+os.path.basename(tbz2) if os.path.realpath(tbz2) != os.path.realpath(etptbz2path): # can convert a file without copying shutil.copy2(tbz2, etptbz2path) info_package = bold(os.path.basename(etptbz2path)) + ": " entropy_client.output( red(info_package + _("Extracting package metadata") + " ..."), importance = 0, level = "info", header = brown(" * "), back = True ) mydata = Spm.extract_package_metadata(etptbz2path) entropy_client.output( red(info_package + _("Package extraction complete")), importance = 0, level = "info", header = brown(" * "), back = True ) # append arbitrary revision mydata['revision'] = etpConst['spmetprev'] mydata['download'] = mydata['download'][:-len(etpConst['packagesext'])] + \ "~" + str(etpConst['spmetprev']) + etpConst['packagesext'] # migrate to the proper format final_tbz2path = os.path.join(os.path.dirname(etptbz2path), os.path.basename(mydata['download'])) shutil.move(etptbz2path, final_tbz2path) etptbz2path = final_tbz2path tmp_dir = etpConst['entropyunpackdir'] tmp_fd, tmp_path = tempfile.mkstemp(prefix="inflate.", dir=tmp_dir) os.close(tmp_fd) # create mydbconn = entropy_client.open_generic_repository(tmp_path) mydbconn.initializeRepository() idpackage = mydbconn.addPackage(mydata, revision = mydata['revision']) mydbconn.close() entropy.tools.aggregate_entropy_metadata(etptbz2path, tmp_path) os.remove(tmp_path) print_info(darkgreen(" * ") + \ darkred("%s: " % (_("Inflated package"),)) + \ etptbz2path) return 0 def deflate_handler(mytbz2s, savedir): # analyze files for tbz2 in mytbz2s: print_info(darkgreen(" * ")+darkred("%s: " % (_("Deflating"),))+tbz2, back = True) save_path = os.path.join(savedir, os.path.basename(tbz2)) ext_rc = entropy.tools.remove_entropy_metadata(tbz2, save_path) if not ext_rc: return 1 tbz2name = os.path.basename(tbz2)[:-len(etpConst['packagesext'])] tbz2name = entropy.dep.remove_tag(tbz2name)+etpConst['packagesext'] newtbz2 = os.path.dirname(tbz2)+os.path.sep+tbz2name print_info(darkgreen(" * ")+darkred("%s: " % (_("Deflated package"),))+newtbz2) return 0 def extract_handler(mytbz2s, savedir): # analyze files for tbz2 in mytbz2s: print_info(darkgreen(" * ")+darkred("%s: " % (_("Extracting Entropy metadata from"),))+tbz2, back = True) dbpath = savedir+os.path.sep+os.path.basename(tbz2) dbpath = dbpath[:-len(etpConst['packagesext'])]+".db" if os.path.isfile(dbpath): os.remove(dbpath) # extract dump_rc = entropy.tools.dump_entropy_metadata(tbz2, dbpath) if not dump_rc: return 1 print_info(darkgreen(" * ")+darkred("%s: " % (_("Extracted Entropy metadata from"),))+dbpath) return 0 def smart_pkg_handler(entropy_client, mypackages): if (not mypackages): print_error(darkred(" * ")+red("%s." % (_("No packages specified"),))) return 1 packages = [] for opt in mypackages: match = entropy_client.atom_match(opt) if match[0] != -1: packages.append(match) else: print_warning(darkred(" * ")+red("%s: " % (_("Cannot find"),))+bold(opt)) pkgs = [] for pkg in packages: if pkg not in pkgs: pkgs.append(pkg) packages = pkgs if not packages: print_error(darkred(" * ")+red("%s." % (_("No valid packages specified"),))) return 2 # print the list print_info(darkgreen(" * ")+red("%s:" % (_("This is the list of the packages that would be merged into a single one"),))) pkgInfo = {} for pkg in packages: dbconn = entropy_client.open_repository(pkg[1]) atom = dbconn.retrieveAtom(pkg[0]) pkgInfo[pkg] = atom print_info(brown("\t[")+red("%s:" % (_("from"),))+pkg[1]+brown("]")+" - "+atom) rc = entropy_client.ask_question(">> %s" % (_("Would you like to create the packages above ?"),)) if rc == _("No"): return 0 print_info(darkgreen(" * ")+red("%s..." % (_("Creating merged Smart Package"),))) rc = smartpackagegenerator(entropy_client, packages) if rc != 0: print_error(darkred(" * ")+red("%s." % (_("Cannot continue"),))) return rc return 0 def smartpackagegenerator(entropy_client, matched_pkgs): fetchdata = [] matchedAtoms = {} for x in matched_pkgs: xdbconn = entropy_client.open_repository(x[1]) matchedAtoms[x] = {} xatom = xdbconn.retrieveAtom(x[0]) xdownload = xdbconn.retrieveDownloadURL(x[0]) xrevision = xdbconn.retrieveRevision(x[0]) matchedAtoms[x]['atom'] = xatom matchedAtoms[x]['download'] = xdownload matchedAtoms[x]['revision'] = xrevision fetchdata.append(x) from entropy.spm.plugins.interfaces.portage_plugin import xpaktools import text_ui # run install_packages with onlyfetch rc = text_ui.install_packages(entropy_client, atomsdata = fetchdata, deps = False, onlyfetch = True) if rc[1] != 0: return rc[0] # create unpack dir and unpack all packages unpackdir = etpConst['entropyunpackdir']+"/smartpackage-"+str(entropy.tools.get_random_number()) while os.path.isdir(unpackdir): unpackdir = etpConst['entropyunpackdir']+"/smartpackage-"+str(entropy.tools.get_random_number()) if os.path.isdir(unpackdir): shutil.rmtree(unpackdir) os.makedirs(unpackdir) os.mkdir(unpackdir+"/content") os.mkdir(unpackdir+"/db") # create master database dbfile = unpackdir+"/db/merged.db" mergeDbconn = entropy_client.open_generic_repository(dbfile, name = "client") mergeDbconn.initializeRepository() tmpdbfile = dbfile+"--readingdata" for package in matched_pkgs: print_info(darkgreen(" * ")+brown(matchedAtoms[package]['atom'])+": "+red(_("collecting Entropy metadata"))) entropy.tools.dump_entropy_metadata( etpConst['entropypackagesworkdir'] + os.path.sep + \ matchedAtoms[package]['download'], tmpdbfile) # read db and add data to mergeDbconn mydbconn = entropy_client.open_generic_repository(tmpdbfile) idpackages = mydbconn.listAllPackageIds() for myidpackage in idpackages: data = mydbconn.getPackageData(myidpackage) if len(idpackages) == 1: # just a plain package that would like to become smart xpakdata = xpaktools.read_xpak( etpConst['entropypackagesworkdir'] + \ os.path.sep + matchedAtoms[package]['download']) else: xpakdata = mydbconn.retrieveSpmMetadata(myidpackage) # already a smart package # add idpk = mergeDbconn.handlePackage(data, forcedRevision = matchedAtoms[package]['revision']) # get the original rev if xpakdata is not None: mergeDbconn.storeSpmMetadata(idpk, xpakdata) mydbconn.close() os.remove(tmpdbfile) # now we have the new database mergeDbconn.close() # merge packages for package in matched_pkgs: print_info(darkgreen(" * ")+brown(matchedAtoms[package]['atom'])+": "+red("unpacking content")) rc = entropy.tools.uncompress_tarball( etpConst['entropypackagesworkdir'] + \ os.path.sep+matchedAtoms[x]['download'], extract_path = os.path.join(unpackdir, "content")) if rc != 0: print_error(darkred(" * ")+red("%s." % (_("Unpack failed due to unknown reasons"),))) return rc smartpackages_dir = os.path.join( etpConst['entropyworkdir'], "smartpackages", etpSys['arch']) if not os.path.isdir(smartpackages_dir): os.makedirs(smartpackages_dir) print_info(darkgreen(" * ")+red(_("Compressing smart package"))) atoms = [] for x in matchedAtoms: atoms.append(matchedAtoms[x]['atom'].split(os.path.sep)[1]) atoms = '+'.join(atoms) smart_package_path = smartpackages_dir + os.path.sep + atoms + \ ".app" rc = entropy.tools.compress_tar_bz2(smart_package_path, unpackdir+"/content") if rc != 0: print_error(darkred(" * ")+red("%s." % (_("Compression failed due to unknown reasons"),))) return rc # adding entropy database if not os.path.isfile(smart_package_path): print_error(darkred(" * ")+red("%s." % (_("Compressed file does not exist"),))) return 1 entropy.tools.aggregate_entropy_metadata(smart_package_path, dbfile) print_info("\t"+smart_package_path) shutil.rmtree(unpackdir, True) return 0