class TestPathMapper(unittest.TestCase): def setUp(self): unittest.TestCase.setUp(self); self._var_root = ""; self._var_cygwin_p = sys.platform.startswith("cygwin"); self.obj = PathMapper(self._var_root, self._var_cygwin_p); def test__init__(self): self.assertTrue(isinstance(self.obj, PathMapper)); self.assertEqual(self._var_root, self.obj.getRoot()); def testAddMapping(self): mount = ( "C:/cygwin/bin on /usr/bin type ntfs (binary,auto){LF}" "C:/cygwin/lib on /usr/lib type ntfs (binary,auto){LF}" "C:/cygwin on / type ntfs (binary,auto){LF}" "C: on /cygdrive/c type ntfs (binary,posix=0,user,noumount,auto){LF}" "".format(LF="\n") ); f = TemporaryFile(mode='w+'); f.writelines(mount); f.seek(0); mtab = f.readlines(); f.close(); mapping = { '/usr/bin/': "C:/cygwin/bin/", '/usr/lib/': "C:/cygwin/lib/", '/cygdrive/c/': "C:/", }; self.obj._addMapping(mtab); self.assertEqual(self.obj.getMap(), mapping); self.assertEqual(self.obj.getMountRoot(), "C:/cygwin/"); def testMapPath(self): if self._var_cygwin_p: self.assertEqual(self.obj.mapPath("/usr/bin/"), "/usr/bin/"); return; mapping = { '/usr/bin/': 'C:/cygwin/bin/', '/usr/lib/': 'C:/cygwin/lib/', '/cygdrive/c/': 'C:/', }; self.obj.setMap(mapping); for cyg in list(mapping.keys()): self.assertEqual(self.obj.mapPath(cyg), mapping[cyg]);
def main(self): # parse command line arguments cap = CygAptArgParser(scriptname=self.getAppName()) args = cap.parse() # initialize main variables with command line arguments and options main_command = args.command main_files = args.package[:] main_files.insert(0, main_command) main_packagename = None if len(args.package) > 0: main_packagename = args.package[0] main_verbose = args.verbose main_download_p = args.download_p main_mirror = args.mirror main_distname = args.distname main_noupdate = args.noupdate main_nodeps_p = args.nodeps_p main_regex_search = args.regex_search main_nobarred = args.force main_verify = args.verify main_nopostinstall = args.nopostinstall main_nopostremove = args.nopostremove main_downloads = None main_dists = 0 main_installed = 0 # locate and parse the configuration file main_cyg_apt_rc = self.getConfigPath() config = None if main_cyg_apt_rc: config = cautils.parse_rc(main_cyg_apt_rc) elif (main_command != "setup"): print("{0}: no .{0}: run \"{0} setup\"".format(self.getAppName()), file=sys.stderr) return 1 # create a CygAptSetup instance and its dependencies main_cygwin_p = (sys.platform == "cygwin") is_64_bit = False if main_cygwin_p: # Running Cygwin python, so python architecture == Cygwin architecture if 2**32 < sys.maxsize: is_64_bit = True elif config and main_command != 'setup': # Running Windows python, so examine cygwin1.dll pathMapper = PathMapper(config.ROOT.rstrip('\\/'), main_cygwin_p) if cautils.pe_is_64_bit(pathMapper.mapPath("/bin/cygwin1.dll")): is_64_bit = True if is_64_bit: main_arch = 'x86_64' else: main_arch = 'x86' cas = CygAptSetup(main_cygwin_p, main_verbose, main_arch) # run command if (main_command == "setup"): cas.setup(args.force) return 0 elif (main_command == "help"): cas.usage(main_cyg_apt_rc) return 0 elif (main_command == "update"): cas.update(main_cyg_apt_rc, main_verify, main_mirror=main_mirror) return 0 # make an update if needed update_not_needed = [ "ball", "find", "help", "purge", "remove", "version", "filelist", "update", "setup", "md5", ] always_update = config.always_update always_update = always_update and\ main_command not in update_not_needed and\ not main_noupdate if always_update: cas.update(main_cyg_apt_rc, main_verify, main_mirror=main_mirror) if main_command and main_command in dir(CygApt): cyg_apt = CygApt( main_packagename, main_files, main_cyg_apt_rc, main_cygwin_p, main_download_p, main_mirror, main_downloads, main_distname, main_nodeps_p, main_regex_search, main_nobarred, main_nopostinstall, main_nopostremove, main_dists, main_installed, self.getAppName(), main_verbose, main_arch, ) getattr(cyg_apt, main_command)() else: cas.usage(main_cyg_apt_rc) return 0
def setUp(self): TestCase.setUp(self); self._var_verbose = False; self._var_cygwin_p = sys.platform.startswith("cygwin"); if not self._var_cygwin_p: self.skipTest("requires cygwin"); setup = CygAptSetup(self._var_cygwin_p, self._var_verbose); setup.setTmpDir(self._dir_tmp); setup.setAppName(self._var_exename); setup.setSetupDir(self._dir_confsetup); setup.getRC().ROOT = self._dir_mtroot; setup._gpgImport(setup.GPG_CYG_PUBLIC_RING_URI); setup.setup(); f = open(self._file_setup_ini, 'w'); f.write(self._var_setupIni.contents); f.close(); f = open(self._file_installed_db, 'w'); f.write(CygApt.INSTALLED_DB_MAGIC); f.close(); self._var_packagename = self._var_setupIni.pkg.name; self._var_files = ["", self._var_packagename]; self._var_download_p = False; self._var_downloads = None; self._var_distname = None; self._var_nodeps_p = False; self._var_regex_search = False; self._var_nobarred = False; self._var_nopostinstall = False; self._var_nopostremove = False; self._var_dists = 0; self._var_installed = 0; self.obj = CygApt( self._var_packagename, self._var_files, self._file_user_config, self._var_cygwin_p, self._var_download_p, self._var_mirror, self._var_downloads, self._var_distname, self._var_nodeps_p, self._var_regex_search, self._var_nobarred, self._var_nopostinstall, self._var_nopostremove, self._var_dists, self._var_installed, self._var_exename, self._var_verbose ); # set attributes rc = ConfigStructure(); rc.cache = self._dir_execache; rc.distname = 'curr'; rc.setup_ini = self._file_setup_ini; rc.ROOT = self._dir_mtroot; rc.always_update = False; rc.mirror = self._var_mirror; self.obj.setRC(rc); self.obj.setDownlaodDir(self._dir_downloads); self.obj.setInstalledDbFile(self._file_installed_db); self.obj.setSetupDir(self._dir_confsetup); pm = PathMapper("", False); pm.setRoot(self._dir_mtroot[:-1]); pm.setMountRoot(self._dir_mtroot); pm.setMap({self._dir_mtroot:self._dir_mtroot}); expected = self._dir_mtroot; ret = pm.mapPath(self._dir_mtroot); self.assertEqual(ret, expected); expected = os.path.join(self._dir_mtroot, "diranme"); ret = pm.mapPath(expected); self.assertEqual(ret, expected); self.obj.setPathMapper(pm); self.obj.setDists(self._var_setupIni.dists.__dict__); self.obj.CYG_POSTINSTALL_DIR = self._dir_postinstall; self.obj.CYG_PREREMOVE_DIR = self._dir_preremove; self.obj.CYG_POSTREMOVE_DIR = self._dir_postremove; self.obj.setDosBash("/usr/bin/bash"); self.obj.setDosLn("/usr/bin/ln"); self.obj.setPrefixRoot(self._dir_mtroot[:-1]); self.obj.setAbsRoot(self._dir_mtroot); self.obj.setInstalled({0:{}}); self.obj.FORCE_BARRED = [self._var_setupIni.barredpkg.name];
class CygAptSetup: RC_OPTIONS = [ 'ROOT', 'mirror', 'cache', 'setup_ini', 'distname', 'barred', 'always_update' ]; RC_COMMENTS = { 'ROOT' : "# The root of your Cygwin installation as a windows path\n", 'mirror' : ( "# URL of your Cygwin mirror: example " "http://mirror.internode.on.net/pub/cygwin/\n" ), 'cache' : ( "# Your package cache as a POSIX path: example " "/e/home/cygwin_package_cache\n" ), "setup_ini" : ( "# setup.ini lists available packages and is " "downloaded from the top level\n" "# of the downloaded mirror. Standard location is " "/etc/setup/setup.ini,\n" "# seutp-2.ini for Cygwin 1.7 Beta\n" ), "distname" : ( "# The distribution, current previous or test " "[curr, prev, test].\n" "# Usually you want the \"curr\" version of a package.\n" ), "barred" : ( "# Packages which cyg-apt can't change under Cygwin " "since it depends on them.\n" "# Run cyg-apt under DOS with -f (force) option to change " "these packages.\n" "# Treat Cygwin core packages with CAUTION.\n" ), "always_update" : ( "# Always update setup.ini before any command " "that uses it. cyg-apt will be\n# faster and use less bandwidth if " "False but you will have to run the update\n# command manually.\n" ), }; RC_REGEX = re.compile(r"^\s*(\w+)\s*=\s*(.*)\s*$"); GPG_GOOD_FINGER = "1169 DF9F 2273 4F74 3AA5 9232 A9A2 62FF 6760 41BA"; GPG_CYG_PUBLIC_RING_URI = "http://cygwin.com/key/pubring.asc"; VERSION = version.__version__; def __init__(self, cygwin_p, verbose): self.__cygwinPlatform = cygwin_p; self.__verbose = verbose; self.__appName = os.path.basename(sys.argv[0]); self.setTmpDir(); self.setPathMapper(); self.__setupDir = "/etc/setup"; self.__rc = ConfigStructure(); self.__rc.ROOT = self.__pm.getMountRoot(); def getCygwinPlatform(self): return self.__cygwinPlatform; def setCygwinPlatform(self, cygwin_p): self.__cygwinPlatform = bool(cygwin_p); def setVerbose(self, verbose): self.__verbose = verbose; def getVerbose(self): return self.__verbose; def getAppName(self): return self.__appName; def setAppName(self, app_name): self.__appName = str(app_name); def getTmpDir(self): return self.__tmpDir; def setTmpDir(self, tmp_dir=None): if tmp_dir: self.__tmpDir = tmp_dir; elif 'TMP' in os.environ: self.__tmpDir = os.environ['TMP']; else: self.__tmpDir = "/usr/share/cyg-apt"; def getPathMapper(self): return self.__pm; def setPathMapper(self, path_mapper=None): if path_mapper: assert isinstance(path_mapper, PathMapper); self.__pm = path_mapper; else: self.__pm = PathMapper("", self.__cygwinPlatform); def getRC(self): return self.__rc; def setRC(self, rc_structure): assert isinstance(rc_structure, ConfigStructure); self.__rc = rc_structure; def getSetupDir(self): return self.__setupDir; def setSetupDir(self, setup_dir): self.__setupDir = str(setup_dir); def _cygwinVersion(self): return float(platform.release()[:3]); def getSetupRc(self, location): filename = os.path.join(location, "setup.rc"); if not (os.path.exists(filename)): return (None, None); f = open(filename); setup_rc = f.readlines(); f.close(); last_cache = None; last_mirror = None; for i in range(0, (len(setup_rc) -1)): if 'last-cache' in setup_rc[i]: last_cache = setup_rc[i+1].strip(); if 'last-mirror' in setup_rc[i]: last_mirror = setup_rc[i+1].strip(); last_cache = cautils.cygpath(last_cache); return (last_cache, last_mirror); def setup(self, force=False): """create cyg-apt configuration file, it overwrite with -f option""" if not self.__cygwinPlatform: msg = "setup outside Cygwin not supported."; raise PlatformException(msg); if "HOME" in os.environ: rc_file = os.path.join( os.environ['HOME'], ".{0}".format(self.__appName) ); else: msg = "Can't locate home directory. Setup failed."; raise EnvironementException(msg); if os.path.exists(rc_file) and not force: msg = "{0} exists, not overwriting.".format(rc_file); raise PathExistsException(msg, code=0); installed_db = os.path.join(self.__setupDir, "installed.db"); missing_cache_marker = ""; missing_mirror_marker = ""; self.__rc.distname = 'curr'; # Refuse to remove/install any package including these substrings # since cyg-apt is dependent on them self.__rc.barred = ""; self.__rc.always_update = False; if not self.__cygwinPlatform: msg = "Setup only supported under Cygwin."; raise PlatformException(msg); (last_cache, last_mirror) = self.getSetupRc(self.__setupDir); if ((not last_cache) or (not last_mirror)): (last_cache, last_mirror) = self._getPre17Last(self.__setupDir); if ((not last_cache) or (not last_mirror)): print("{0}: {1}/setup.rc not found. Please edit {2} to "\ "provide mirror and cache.".format( self.__appName, self.__setupDir, rc_file )); last_cache = missing_cache_marker; last_mirror = missing_mirror_marker; self.__rc.mirror = last_mirror; self.__rc.cache = last_cache; self.__rc.setup_ini = "{0}/setup.ini".format(self.__setupDir); contents = ""; for i in self.__rc.__dict__: if i in list(self.RC_COMMENTS.keys()): contents += self.RC_COMMENTS[i]; contents += "{0}=\"{1}\"\n\n".format(i, self.__rc.__dict__[i]); f = open(rc_file, 'w'); f.write(contents); f.close(); print("{0}: creating {1}".format(self.__appName, rc_file)); if not os.path.isdir(self.__rc.ROOT): msg = "{0} no root directory".format(self.__rc.ROOT); raise UnexpectedValueException(msg); if not os.path.isdir(self.__setupDir): sys.stderr.write('creating {0}\n'.format(self.__setupDir)); os.makedirs(self.__setupDir); if not os.path.isfile(installed_db): self._writeInstalled(installed_db); if not os.path.isfile(self.__rc.setup_ini): sys.stderr.write('getting {0}\n'.format(self.__rc.setup_ini)); self.update(rc_file, True); def usage(self, cyg_apt_rc=None): print("{0}, version {1}".format(self.__appName, self.VERSION)); print(copying.help_message, end="\n\n"); if (cyg_apt_rc): print("Configuration: {0}".format(cyg_apt_rc)); print("Usage: {0} [OPTION]... COMMAND [PACKAGE]...".format( self.__appName )); print("\n Commands:"); members = []; for m in inspect.getmembers(CygAptSetup) + inspect.getmembers(CygApt): if isinstance(m[1], type(self.usage)) and m[1].__doc__: members.append(m); pad = max(len(m[0]) for m in members); for m in members: print(" {0} : {1}".format(m[0].ljust(pad), m[1].__doc__)); sys.stdout.write( "{LF}" " Options:{LF}" " -d, --download download only{LF}" " -h, --help show brief usage{LF}" " -m, --mirror=URL use mirror{LF}" " -t, --dist=NAME set dist name (curr, test, prev){LF}" " -x, --no-deps ignore dependencies{LF}" " -s, --regexp search as regex pattern{LF}" " -f, --nobarred add/remove packages cyg-apt depends on{LF}" " -X, --no-verify do not verify setup.ini signatures{LF}" " -y, --nopostinstall do not run postinstall scripts{LF}" " -z, --nopostremove do not run preremove/postremove scripts{LF}" " -q, --quiet loggable output - no progress indicator{LF}" "".format(LF="\n") ); def update(self, cyg_apt_rc, verify, main_mirror=None): """fetch current package database from mirror""" sig_name = None; f = open(cyg_apt_rc); lines = f.readlines(); f.close(); for i in lines: result = self.RC_REGEX.search(i); if result: k = result.group(1); v = result.group(2); if k in self.__rc.__dict__: self.__rc.__dict__[k] = eval(v); if(not self.__cygwinPlatform): self.__pm = PathMapper(self.__rc.ROOT[:-1], False); setup_ini = self.__pm.mapPath(self.__rc.setup_ini); if (main_mirror): mirror = main_mirror; else: mirror = self.__rc.mirror; downloads = os.path.join( self.__pm.mapPath(self.__rc.cache), urllib.quote(mirror, '').lower() ); if not mirror[-1] == "/": sep = "/"; else: sep = ""; setup_ini_names = [ os.path.basename(setup_ini).replace(".ini", ".bz2"), os.path.basename(setup_ini) ]; bag = zip(setup_ini_names, list(range(len(setup_ini_names)))); for (setup_ini_name, index) in bag: setup_ini_url = '{0}{1}{2}'.format(mirror, sep, setup_ini_name); try: cautils.uri_get( self.__tmpDir, setup_ini_url, verbose=self.__verbose ); except ApplicationException as e: # Failed to find a possible .ini if index == len(setup_ini_names) - 1: raise e; else: continue; # Not an error to fail to find the first one # Take the first one we find break; if setup_ini_name[-4:] == ".bz2": bz_file = os.path.join(self.__tmpDir, setup_ini_name); f = open(bz_file, "rb"); compressed = f.read(); f.close(); decomp = bz2.decompress(compressed); os.remove(bz_file); setup_ini_name = os.path.basename(setup_ini); f = open(os.path.join(self.__tmpDir, setup_ini_name), "wb"); f.write(decomp); f.close(); if not self.__cygwinPlatform: sys.stderr.write("WARNING can't verify setup.ini outside Cygwin.\n"); verify = False; if verify: sig_name = "{0}.sig".format(setup_ini_name); sig_url = "{0}{1}{2}".format(mirror, sep, sig_name); try: cautils.uri_get(self.__tmpDir, sig_url, verbose=self.__verbose); except RequestException as e: msg = ( "Failed to download signature {0} Use -X to ignore " "signatures.".format(sig_url) ); raise RequestException(msg, previous=e); if self.__cygwinPlatform: gpg_path = "gpg "; else: if self._cygwinVersion() < 1.7: gpg_path = "/usr/bin/gpg "; else: gpg_path = "/usr/local/bin/gpg "; cmd = gpg_path + "--verify --no-secmem-warning "; cmd += "{0}/{1} ".format(self.__tmpDir, sig_name); cmd += "{0}/{1} ".format(self.__tmpDir, setup_ini_name); p = subprocess.Popen( cmd, shell=True, stderr=subprocess.PIPE ); p.wait(); verify = p.stderr.read(); if isinstance(verify, bytes): marker = self.GPG_GOOD_FINGER.encode(); else: marker = self.GPG_GOOD_FINGER; if not marker in verify: msg = ( "{0} not signed by Cygwin's public key. " "Use -X to ignore signatures.".format(setup_ini_url) ); raise SignatureException(msg); if not os.path.exists(downloads): os.makedirs(downloads); shutil.copy( os.path.join(self.__tmpDir, setup_ini_name), os.path.join(downloads, setup_ini_name) ); if os.path.exists(setup_ini): shutil.copy(setup_ini, "{0}.bak".format(setup_ini)); shutil.copy( os.path.join(downloads, setup_ini_name), setup_ini ); if os.path.exists(os.path.join(self.__tmpDir, setup_ini_name)): os.remove(os.path.join(self.__tmpDir, setup_ini_name)); if sig_name: if os.path.exists(os.path.join(self.__tmpDir, sig_name)): os.remove(os.path.join(self.__tmpDir, sig_name)); def _getPre17Last(self, location): if not os.path.exists(os.path.join(location, "last-mirror")) \ or not os.path.exists(os.path.join(location, "last-cache")): return (None, None); else: f = open(os.path.join(location, "last-cache")); last_cache = f.read().strip(); f.close(); last_cache = cautils.cygpath(last_cache); f = open(os.path.join(location, "last-mirror")); last_mirror = f.read().strip(); f.close(); return (last_cache, last_mirror); def _writeInstalled(self, installed_db): if not self.__cygwinPlatform: raise PlatformException( "fail to create {0} only supported under Cygwin." "".format(installed_db) ); sys.stderr.write("creating {0} ... ".format(installed_db)); db_contents = CygApt.INSTALLED_DB_MAGIC; cygcheck_path = self.__pm.mapPath("/bin/cygcheck"); if os.path.exists(cygcheck_path): cmd = cygcheck_path + " -cd "; proc = subprocess.Popen( cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE ); if proc.wait(): raise ProcessException(proc.stderr.readlines()); lines = proc.stdout.readlines(); # remove first two lines pkgs = lines[2:]; for pkg in pkgs: pkg = pkg.split(); db_contents += "{0} {0}-{1}.tar.bz2 0\n".format(pkg[0], pkg[1]); f = open(installed_db, 'w'); f.write(db_contents); f.close(); sys.stderr.write("OK\n"); def _gpgImport(self, uri): if not self.__cygwinPlatform: return; cautils.uri_get(self.__tmpDir, uri, verbose=self.__verbose); tmpfile = os.path.join(self.__tmpDir, os.path.basename(uri)); cmd = "gpg "; cmd += "--no-secmem-warning "; cmd += "--import {0}".format(tmpfile); p = subprocess.Popen( cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE ); if p.wait(): raise ProcessException(p.stderr.readlines());
class CygAptSetup: RC_OPTIONS = [ 'ROOT', 'mirror', 'cache', # BC layer for `setup_ini` configuration field 'setup_ini', 'distname', 'barred', 'always_update' ] RC_COMMENTS = { 'ROOT': "# The root of your Cygwin installation as a windows path\n", 'mirror': ("# URL of your Cygwin mirror: example " "http://mirror.internode.on.net/pub/cygwin/\n"), 'cache': ("# Your package cache as a POSIX path: example " "/e/home/cygwin_package_cache\n"), # BC layer for `setup_ini` configuration field "setup_ini": ("# setup.ini lists available packages and is " "downloaded from the top level\n" "# of the downloaded mirror. Standard location is " "/etc/setup/setup.ini,\n" "# seutp-2.ini for Cygwin 1.7 Beta\n" "# Deprecated since version 1.1 and will be removed in 2.0.\n" "# "), "distname": ("# The distribution, current previous or test " "[curr, prev, test].\n" "# Usually you want the \"curr\" version of a package.\n"), "barred": ("# Packages which cyg-apt can't change under Cygwin " "since it depends on them.\n" "# Run cyg-apt under DOS with -f (force) option to change " "these packages.\n" "# Treat Cygwin core packages with CAUTION.\n"), "always_update": ("# Always update setup.ini before any command " "that uses it. cyg-apt will be\n# faster and use less bandwidth if " "False but you will have to run the update\n# command manually.\n"), } GPG_GOOD_FINGER = "1169 DF9F 2273 4F74 3AA5 9232 A9A2 62FF 6760 41BA" GPG_CYG_PUBLIC_RING_URI = "http://cygwin.com/key/pubring.asc" VERSION = version.__version__ def __init__(self, cygwin_p, verbose, arch="x86"): self.__cygwinPlatform = cygwin_p self.__verbose = verbose self.__appName = os.path.basename(sys.argv[0]) self.setTmpDir() self.setPathMapper() self.__setupDir = "/etc/setup" self.__rc = ConfigStructure() self.__arch = arch self.__rc.ROOT = self.__pm.getMountRoot() def getCygwinPlatform(self): return self.__cygwinPlatform def setCygwinPlatform(self, cygwin_p): self.__cygwinPlatform = bool(cygwin_p) def setVerbose(self, verbose): self.__verbose = verbose def getVerbose(self): return self.__verbose def getAppName(self): return self.__appName def setAppName(self, app_name): self.__appName = str(app_name) def getTmpDir(self): return self.__tmpDir def setTmpDir(self, tmp_dir=None): if tmp_dir: self.__tmpDir = tmp_dir elif 'TMP' in os.environ: self.__tmpDir = os.environ['TMP'] else: self.__tmpDir = "/usr/share/cyg-apt" def getPathMapper(self): return self.__pm def setPathMapper(self, path_mapper=None): if path_mapper: assert isinstance(path_mapper, PathMapper) self.__pm = path_mapper else: self.__pm = PathMapper("", self.__cygwinPlatform) def getRC(self): return self.__rc def setRC(self, rc_structure): assert isinstance(rc_structure, ConfigStructure) self.__rc = rc_structure def getSetupDir(self): return self.__setupDir def setSetupDir(self, setup_dir): self.__setupDir = str(setup_dir) def setArchitecture(self, architecture): self.__arch = architecture def _cygwinVersion(self): return float(platform.release()[:3]) def getSetupRc(self, location): filename = os.path.join(location, "setup.rc") if not (os.path.exists(filename)): return (None, None) f = open(filename) setup_rc = f.readlines() f.close() last_cache = None last_mirror = None for i in range(0, (len(setup_rc) - 1)): if 'last-cache' in setup_rc[i]: last_cache = setup_rc[i + 1].strip() if 'last-mirror' in setup_rc[i]: last_mirror = setup_rc[i + 1].strip() last_cache = self.__pm.mapPath(last_cache) return (last_cache, last_mirror) def setup(self, force=False): """create cyg-apt configuration file, it overwrite with -f option""" if not self.__cygwinPlatform: msg = "setup outside Cygwin not supported." raise PlatformException(msg) if "HOME" in os.environ: rc_file = os.path.join(os.environ['HOME'], ".{0}".format(self.__appName)) else: msg = "Can't locate home directory. Setup failed." raise EnvironementException(msg) if os.path.exists(rc_file) and not force: msg = "{0} exists, not overwriting.".format(rc_file) raise PathExistsException(msg, code=0) installed_db = os.path.join(self.__setupDir, "installed.db") missing_cache_marker = "" missing_mirror_marker = "" self.__rc.distname = 'curr' # Refuse to remove/install any package including these substrings # since cyg-apt is dependent on them self.__rc.barred = "" self.__rc.always_update = False if not self.__cygwinPlatform: msg = "Setup only supported under Cygwin." raise PlatformException(msg) (last_cache, last_mirror) = self.getSetupRc(self.__setupDir) if ((not last_cache) or (not last_mirror)): (last_cache, last_mirror) = self._getPre17Last(self.__setupDir) if ((not last_cache) or (not last_mirror)): print("{0}: {1}/setup.rc not found. Please edit {2} to "\ "provide mirror and cache. See cygwin.com/mirrors.html "\ "for the list of mirrors.".format( self.__appName, self.__setupDir, rc_file )) last_cache = missing_cache_marker last_mirror = missing_mirror_marker self.__rc.mirror = last_mirror self.__rc.cache = last_cache # BC layer for `setup_ini` configuration field self.__rc.setup_ini = "{0}/setup.ini".format(self.__setupDir) contents = "" for i in self.__rc.__dict__: if i in list(self.RC_COMMENTS.keys()): contents += self.RC_COMMENTS[i] contents += "{0}=\"{1}\"\n\n".format(i, self.__rc.__dict__[i]) f = open(rc_file, 'w') f.write(contents) f.close() print("{0}: creating {1}".format(self.__appName, rc_file)) if not os.path.isdir(self.__rc.ROOT): msg = "{0} no root directory".format(self.__rc.ROOT) raise UnexpectedValueException(msg) if not os.path.isdir(self.__setupDir): sys.stderr.write('creating {0}\n'.format(self.__setupDir)) os.makedirs(self.__setupDir) if not os.path.isfile(installed_db): self._writeInstalled(installed_db) setupIniPath = os.path.join( self.__pm.mapPath(self.__rc.cache), urllib.quote( self.__rc.mirror + ('' if self.__rc.mirror.endswith('/') else '/'), '').lower(), self.__arch, 'setup.ini', ) if not os.path.isfile(setupIniPath): sys.stderr.write('getting {0}\n'.format(setupIniPath)) self.update(rc_file, True) def usage(self, cyg_apt_rc=None): print("{0}, version {1}".format(self.__appName, self.VERSION)) print(copying.help_message, end="\n\n") if (cyg_apt_rc): print("Configuration: {0}".format(cyg_apt_rc)) print("Usage: {0} [OPTION]... COMMAND [PACKAGE]...".format( self.__appName)) print("\n Commands:") members = [] for m in inspect.getmembers(CygAptSetup) + inspect.getmembers(CygApt): if isinstance(m[1], type(self.usage)) and m[1].__doc__: members.append(m) pad = max(len(m[0]) for m in members) for m in members: print(" {0} : {1}".format(m[0].ljust(pad), m[1].__doc__)) sys.stdout.write( "{LF}" " Options:{LF}" " -d, --download download only{LF}" " -h, --help show brief usage{LF}" " -m, --mirror=URL use mirror{LF}" " -t, --dist=NAME set dist name (curr, test, prev){LF}" " -x, --no-deps ignore dependencies{LF}" " -s, --regexp search as regex pattern{LF}" " -f, --nobarred add/remove packages cyg-apt depends on{LF}" " -X, --no-verify do not verify setup.ini signatures{LF}" " -y, --nopostinstall do not run postinstall scripts{LF}" " -z, --nopostremove do not run preremove/postremove scripts{LF}" " -q, --quiet loggable output - no progress indicator{LF}" "".format(LF="\n")) def update(self, cyg_apt_rc, verify, main_mirror=None): """fetch current package database from mirror""" sig_name = None self.__rc = cautils.parse_rc(cyg_apt_rc) if (not self.__cygwinPlatform): self.__pm = PathMapper(self.__rc.ROOT[:-1], False) if (main_mirror): mirror = main_mirror else: mirror = self.__rc.mirror if not mirror: raise UnexpectedValueException( "A mirror must be specified on the configuration file \"{0}\" " "or with the command line option \"--mirror\". " "See cygwin.com/mirrors.html for the list of mirrors." "".format(cyg_apt_rc)) if not mirror[-1] == "/": sep = "/" else: sep = "" setup_ini_names = [ "setup.bz2", "setup.ini", ] bag = zip(setup_ini_names, list(range(len(setup_ini_names)))) platform_dir = self.__arch + "/" for (setup_ini_name, index) in bag: setup_ini_url = '{0}{1}{2}{3}'.format(mirror, sep, platform_dir, setup_ini_name) try: cautils.uri_get(self.__tmpDir, setup_ini_url, verbose=self.__verbose) except ApplicationException as e: # Failed to find a possible .ini if index == len(setup_ini_names) - 1: raise e else: continue # Not an error to fail to find the first one # Take the first one we find break if setup_ini_name[-4:] == ".bz2": bz_file = os.path.join(self.__tmpDir, setup_ini_name) f = open(bz_file, "rb") compressed = f.read() f.close() decomp = bz2.decompress(compressed) os.remove(bz_file) setup_ini_name = "setup.ini" f = open(os.path.join(self.__tmpDir, setup_ini_name), "wb") f.write(decomp) f.close() if not self.__cygwinPlatform: sys.stderr.write( "WARNING can't verify setup.ini outside Cygwin.\n") verify = False if verify: sig_name = "{0}.sig".format(setup_ini_name) sig_url = "{0}{1}{2}{3}".format(mirror, sep, platform_dir, sig_name) try: cautils.uri_get(self.__tmpDir, sig_url, verbose=self.__verbose) except RequestException as e: msg = ("Failed to download signature {0} Use -X to ignore " "signatures.".format(sig_url)) raise RequestException(msg, previous=e) if self.__cygwinPlatform: gpg_path = "gpg" else: if self._cygwinVersion() < 1.7: gpg_path = "/usr/bin/gpg" else: gpg_path = "/usr/local/bin/gpg" cmd = [gpg_path, "--verify", "--no-secmem-warning"] cmd.append("{0}/{1}".format(self.__tmpDir, sig_name)) cmd.append("{0}/{1}".format(self.__tmpDir, setup_ini_name)) p = Process(cmd) p.run() verify = p.getErrorOutput() if isinstance(verify, bytes): marker = self.GPG_GOOD_FINGER.encode() else: marker = self.GPG_GOOD_FINGER if not marker in verify: msg = ("{0} not signed by Cygwin's public key. " "Use -X to ignore signatures.".format(setup_ini_url)) raise SignatureException(msg) downloads = os.path.join( self.__pm.mapPath(self.__rc.cache), urllib.quote(mirror + ('' if mirror.endswith('/') else '/'), '').lower(), platform_dir, ) if not os.path.exists(downloads): os.makedirs(downloads) shutil.copy(os.path.join(self.__tmpDir, setup_ini_name), os.path.join(downloads, setup_ini_name)) # BC layer for `setup_ini` configuration field if self.__rc.setup_ini: setup_ini = self.__pm.mapPath(self.__rc.setup_ini) if os.path.exists(setup_ini): shutil.copy(setup_ini, "{0}.bak".format(setup_ini)) shutil.copy(os.path.join(downloads, setup_ini_name), setup_ini) if os.path.exists(os.path.join(self.__tmpDir, setup_ini_name)): os.remove(os.path.join(self.__tmpDir, setup_ini_name)) if sig_name: if os.path.exists(os.path.join(self.__tmpDir, sig_name)): os.remove(os.path.join(self.__tmpDir, sig_name)) def _getPre17Last(self, location): if not os.path.exists(os.path.join(location, "last-mirror")) \ or not os.path.exists(os.path.join(location, "last-cache")): return (None, None) else: f = open(os.path.join(location, "last-cache")) last_cache = f.read().strip() f.close() last_cache = self.__pm.mapPath(last_cache) f = open(os.path.join(location, "last-mirror")) last_mirror = f.read().strip() f.close() return (last_cache, last_mirror) def _writeInstalled(self, installed_db): if not self.__cygwinPlatform: raise PlatformException( "fail to create {0} only supported under Cygwin." "".format(installed_db)) sys.stderr.write("creating {0} ... ".format(installed_db)) db_contents = CygApt.INSTALLED_DB_MAGIC cygcheck_path = self.__pm.mapPath("/bin/cygcheck") if os.path.exists(cygcheck_path): cmd = [cygcheck_path, "-cd"] proc = Process(cmd) proc.mustRun() lines = proc.getOutput().splitlines(True) # remove first two lines pkgs = lines[2:] for pkg in pkgs: pkg = pkg.split() db_contents += "{0} {0}-{1}.tar.bz2 0\n".format( pkg[0], pkg[1]) f = open(installed_db, 'w') f.write(db_contents) f.close() sys.stderr.write("OK\n") def _gpgImport(self, uri): if not self.__cygwinPlatform: return cautils.uri_get(self.__tmpDir, uri, verbose=self.__verbose) tmpfile = os.path.join(self.__tmpDir, os.path.basename(uri)) cmd = ["gpg"] cmd.append("--no-secmem-warning") cmd += ["--import", tmpfile] Process(cmd).mustRun()
def _createCygApt(self): """Creates a CygApt instance. @return: CygApt """ cygapt = CygApt( self._var_packagename, self._var_files, self._file_user_config, self._var_cygwin_p, self._var_download_p, self._var_mirror, self._var_downloads, self._var_distname, self._var_nodeps_p, self._var_regex_search, self._var_nobarred, self._var_nopostinstall, self._var_nopostremove, self._var_dists, self._var_installed, self._var_exename, self._var_verbose, self._var_arch, self._dir_confsetup, ); # set attributes rc = ConfigStructure(); rc.cache = self._dir_execache; rc.distname = 'curr'; # BC layer for `setup_ini` configuration field del rc.__dict__['setup_ini']; rc.ROOT = self._dir_mtroot; rc.always_update = False; rc.mirror = self._var_mirror; cygapt.setRC(rc); cygapt.setInstalledDbFile(self._file_installed_db); self.assertEqual(self._dir_confsetup, cygapt.getSetupDir()); pm = PathMapper("", False); pm.setRoot(self._dir_mtroot[:-1]); pm.setMountRoot(self._dir_mtroot); pm.setMap({self._dir_mtroot:self._dir_mtroot}); expected = self._dir_mtroot; ret = pm.mapPath(self._dir_mtroot); self.assertEqual(ret, expected); expected = os.path.join(self._dir_mtroot, "diranme"); ret = pm.mapPath(expected); self.assertEqual(ret, expected); cygapt.setPathMapper(pm); cygapt.setDists(self._var_setupIni.dists.__dict__); if self._var_cygwin_p : cygapt.CYG_POSTINSTALL_DIR = self._dir_postinstall; cygapt.CYG_PREREMOVE_DIR = self._dir_preremove; cygapt.CYG_POSTREMOVE_DIR = self._dir_postremove; # requires bash, ln and xz on PATH cygapt.setDosBash("bash"); cygapt.setDosLn("ln"); cygapt.setDosXz('xz'); cygapt.setDosDash('dash'); cygapt.setPrefixRoot(self._dir_mtroot[:-1]); cygapt.setAbsRoot(self._dir_mtroot); cygapt.setInstalled({0:{}}); cygapt.FORCE_BARRED.extend([ self._var_setupIni.barredpkg.name, ]); return cygapt;
class CygApt: INSTALLED_DB_MAGIC = "INSTALLED.DB 2\n" DIST_NAMES = ('curr', 'test', 'prev') FORCE_BARRED = ["python", "python-argparse", "gnupg", "xz"] SH_OPTIONS = ["--norc", "--noprofile"] DASH_OPTIONS = [] CMD_OPTIONS = ["/V:ON", "/E:ON", "/C"] CYG_POSTINSTALL_DIR = "/etc/postinstall" CYG_PREREMOVE_DIR = "/etc/preremove" CYG_POSTREMOVE_DIR = "/etc/postremove" def __init__( self, main_packagename, main_files, main_cyg_apt_rc, main_cygwin_p, main_download_p, main_mirror, main_downloads, main_distname, main_nodeps_p, main_regex_search, main_nobarred, main_nopostinstall, main_nopostremove, main_dists, main_installed, main_scriptname, main_verbose, arch, setupDir="/etc/setup", ): # Define private properties self.__ballTarget = 'install' self.__regexSearch = main_regex_search self.__noBarred = main_nobarred self.__noPostInstall = main_nopostinstall self.__noPostRemove = main_nopostremove self.__appName = main_scriptname self.__files = main_files self.__downloadOnly = main_download_p self.__downloadDir = main_downloads self.__noDeps = main_nodeps_p self.__rcFile = main_cyg_apt_rc self.__verbose = main_verbose self.__setupDir = setupDir self.__rc = ConfigStructure() self.__setupIniPath = None self.__arch = arch # Init self.setPkgName(main_packagename) self.setCygwinPlatform(main_cygwin_p) self.setDists(main_dists) self.setInstalled(main_installed) # Read in our configuration self.getRessource(self.__rcFile) # Now we have a path mapper, check setup.exe is not running self._checkForSetupExe() # DOS specific if not self.__cygwinPlatform: self.__lnExists = os.path.exists("{0}/bin/ln.exe".format( self.__prefixRoot)) else: self.__lnExists = True # Overrides to the .rc if (main_mirror): self.__rc.mirror = main_mirror self.__downloadDir = os.path.join( self.__rc.cache, urllib.quote( self.__rc.mirror + ('' if self.__rc.mirror.endswith('/') else '/'), '').lower()) if (main_distname): self.__rc.distname = main_distname if not (os.path.isfile(self.__installedDbFile) \ or os.path.isfile(self.__setupIniPath)): msg = "{0} no such file, run {1} setup?".format( self.__installedDbFile, self.__appName) raise PackageCacheException(msg) else: self._getSetupIni() self.getInstalled() def getPkgName(self): return self.__pkgName def setPkgName(self, pkg_name): self.__pkgName = str(pkg_name) def getCygwinPlatform(self): return self.__cygwinPlatform def setCygwinPlatform(self, cygwin_p): self.__cygwinPlatform = bool(cygwin_p) def getDownloadDir(self): return self.__downloadDir def setDownlaodDir(self, download_dir): self.__downloadDir = str(download_dir) def getDists(self): return self.__dists def setDists(self, dists): self.__dists = dists def getSetupDir(self): return self.__setupDir def setSetupDir(self, setup_dir): self.__setupDir = str(setup_dir) def getInstalledDbFile(self): return self.__installedDbFile def setInstalledDbFile(self, installed_db_file): self.__installedDbFile = str(installed_db_file) def getDosBash(self): return self.__dosBash def setDosBash(self, dos_bash): self.__dosBash = str(dos_bash) def getDosLn(self): return self.__dosLn def setDosLn(self, dos_ln): self.__dosLn = str(dos_ln) def getDosXz(self): return self.__dosXz def setDosXz(self, dos_xz): self.__dosXz = str(dos_xz) def getDosDash(self): return self.__dosDash def setDosDash(self, dosDash): self.__dosDash = str(dosDash) def getPrefixRoot(self): return self.__prefixRoot def setPrefixRoot(self, prefix_root): self.__prefixRoot = prefix_root def getAbsRoot(self): return self.__absRoot def setAbsRoot(self, absolute_root): self.__absRoot = absolute_root def getPathMapper(self): return self.__pm def setPathMapper(self, path_mapper): assert isinstance(path_mapper, PathMapper) self.__pm = path_mapper def getRC(self): return self.__rc def setRC(self, rc_structure): assert isinstance(rc_structure, ConfigStructure) self.__rc = rc_structure def _checkForSetupExe(self): # It's far from bulletproof, but it's surprisingly hard to detect # setup.exe running since it doesn't lock any files. p = Process([self.__pm.mapPath("/bin/ps"), "-W"]) p.run() psout = p.getOutput().splitlines(True) setup_re = re.compile( r"(?<![a-z0-9_ -])setup(|-1\.7|-x86|-x86_64)\.exe", re.IGNORECASE) for l in psout: m = setup_re.search(l) if m: raise AppConflictException("Please close {0} while " "running {1}".format( m.group(0), self.__appName)) def _versionToString(self, t): def try_itoa(x): if isinstance(x, int): return "{0:d}".format(x) return x return "{0}-{1}".format(".".join(list(map(try_itoa, t[:-1]))), t[-1]) def _stringToVersion(self, s): s = re.sub(r"([^0-9][^0-9]*)", " \\1 ", s) s = re.sub(r"[ _.-][ _.-]*", " ", s) def try_atoi(x): if re.match(r"^[0-9]*$", x): return int(x) return x return tuple(map(try_atoi, (s.split(' ')))) def _splitBall(self, p): m = re.match(r"^(.*)-([0-9].*-[0-9]+)(.tar.(bz2|xz))?$", p) if not m: print("splitBall: {0}".format(p)) return (p[:2], (0, 0)) t = (m.group(1), self._stringToVersion(m.group(2))) return t def _joinBall(self, t): return "{0}-{1}".format(t[0], self._versionToString(t[1])) def _debug(self, s): s def help(self, ): """this help message""" pass def _getSetupIni(self): if self.__dists: return self.__dists = { 'test': {}, 'curr': {}, 'prev': {} } f = open(self.__setupIniPath) contents = f.read() f.close() chunks = contents.split("\n\n@ ") for i in chunks[1:]: lines = i.split("\n") name = lines[0].strip() self._debug("package: {0}".format(name)) packages = self.__dists['curr'] records = { 'sdesc': name } j = 1 while j < len(lines) and lines[j].strip(): self._debug("raw: {0}".format(lines[j])) if lines[j][0] == '#': j = j + 1 continue elif lines[j][0] == '[': self._debug('dist: {0}'.format(lines[j][1:5])) packages[name] = records.copy() packages = self.__dists[lines[j][1:5]] j = j + 1 continue duo = lines[j].split(": ", 1) key = duo[0].strip() value = duo[1].strip() if value.find('"') != -1 \ and value.find('"', value.find('"') + 1) == -1: while True: j = j + 1 value += "\n" + lines[j] if lines[j].find('"') != -1: break records[key] = value j = j + 1 packages[name] = records def _getUrl(self): if self.__pkgName not in self.__dists[self.__rc.distname] \ or self.__ballTarget not in self.__dists[self.__rc.distname][self.__pkgName]: self._printErr(self._noPackage()) install = 0 for d in self.DIST_NAMES: if self.__pkgName in self.__dists[d] \ and self.__ballTarget in self.__dists[d][self.__pkgName]: install = self.__dists[d][self.__pkgName][ self.__ballTarget] print("warning: using [{0}]\n".format(d), file=sys.stderr) break if not install: raise PackageException("{0} is not in {1}".format( self.__pkgName, self.__setupIniPath)) else: install = self.__dists[self.__rc.distname][self.__pkgName][ self.__ballTarget] filename, size, md5 = install.split() return filename, md5 def url(self): """print tarball url""" if not self.__pkgName: raise CommandLineException("url command requires a package name") print("{0}/{1}".format(self.__rc.mirror, self._getUrl()[0])) def getBall(self): url, md5 = self._getUrl() return os.path.join(self.__downloadDir, url) def ball(self): """print tarball name""" print(self.getBall()) def _doDownload(self): url, md5 = self._getUrl() directory = os.path.join(self.__downloadDir, os.path.split(url)[0]) if not os.path.exists(self.getBall()) or not self._checkMd5(): if not os.path.exists(directory): os.makedirs(directory) status = cautils.uri_get(directory, "{0}/{1}".format(self.__rc.mirror, url)) if status: raise PackageCacheException( "didn't find {0} " "on mirror {1}: possible mismatch between setup.ini and " "mirror requiring {2} update?".format( self.__pkgName, self.__rc.mirror, self.__appName)) def download(self): """download package (only, do not install)""" self._doDownload() self.ball() self.checksum() def _noPackage(self): return "{0} is not on mirror {1} in [{2}].".format( self.__pkgName, self.__rc.mirror, self.__rc.distname) # return an array contents all dependencies of self.__pkgName def getRequires(self): # Looking for dependencies on curr not prev or test dist = self.__dists['curr'] if self.__pkgName not in self.__dists[self.__rc.distname]: raise PackageException(self._noPackage()) if self.__noDeps: return [] reqs = { self.__pkgName: 0 } if self.__ballTarget == 'source' \ and 'external-source' in dist[self.__pkgName]: reqs[dist[self.__pkgName]['external-source']] = 0 n = 0 while len(reqs) > n: n = len(reqs) for i in list(reqs.keys()): if i not in dist: sys.stderr.write("error: {0} not in [{1}]\n".format( i, self.__rc.distname)) if i != self.__pkgName: del reqs[i] continue reqs[i] = '0' p = dist[i] if 'requires' not in p: continue update_list = [(x, 0) for x in p['requires'].split()] reqs.update(update_list) # Delete the ask package it is not require by it self (joke) reqs.pop(self.__pkgName) rlist = sorted(reqs.keys()) return rlist def requires(self): """print requires: for package""" reqs = self.getRequires() if len(reqs) == 0: print("No dependencies for package {0}".format(self.__pkgName)) else: print("\n".join(reqs)) def getInstalled(self): if self.__installed: return self.__installed self.__installed = { 0: {} } f = open(self.__installedDbFile) lines = f.readlines() f.close() for i in lines[1:]: name, ball, status = i.split() self.__installed[int(status)][name] = ball return self.__installed def setInstalled(self, installed): self.__installed = installed def _writeInstalled(self): file_db = open(self.__installedDbFile, 'w') file_db.write(self.INSTALLED_DB_MAGIC) lines = [] for x in list(self.__installed[0].keys()): lines.append("{0} {1} 0\n".format(x, self.__installed[0][x])) file_db.writelines(lines) if file_db.close(): raise IOError(self.__installedDbFile) def getField(self, field, default=''): for d in (self.__rc.distname, ) + self.DIST_NAMES: if self.__pkgName in self.__dists[d] \ and field in self.__dists[d][self.__pkgName]: return self.__dists[d][self.__pkgName][field] return default def _psort(self, lst): lst.sort() return lst def _preverse(self, lst): lst.reverse() return lst def list(self): """list installed packages""" print("--- Installed packages ---") for self.__pkgName in self._psort(list(self.__installed[0].keys())): ins = self.getInstalledVersion() new = 0 if self.__pkgName in self.__dists[self.__rc.distname] \ and self.__ballTarget in self.__dists[self.__rc.distname][self.__pkgName]: new = self.getVersion() s = "{0:<19} {1:<15}".format(self.__pkgName, self._versionToString(ins)) if new and new != ins: s += "({0})".format(self._versionToString(new)) print(s) def filelist(self): """list files installed by given packages""" if not self.__pkgName: msg = "no package name given." raise CommandLineException(msg) else: print("\n".join(self.getFileList())) def _postInstall(self): self._runAll(self.CYG_POSTINSTALL_DIR) def _postRemove(self): if len(self.__files[1:]) == 0: msg = "must specify package to run postremove." raise CommandLineException(msg) else: for self.__pkgName in self.__files[1:]: preremove_sh = os.path.join(self.CYG_PREREMOVE_DIR, "{0}.sh".format(self.__pkgName)) postremove_sh = os.path.join(self.CYG_POSTREMOVE_DIR, "{0}.sh".format(self.__pkgName)) self._runScript(preremove_sh) self._runScript(postremove_sh) def getVersion(self): if self.__pkgName not in self.__dists[self.__rc.distname] \ or self.__ballTarget not in self.__dists[self.__rc.distname][self.__pkgName]: self._printErr(self._noPackage()) return (0, 0) package = self.__dists[self.__rc.distname][self.__pkgName] if 'ver' not in package: filename = package[self.__ballTarget].split()[0] ball = os.path.split(filename)[1] package['ver'] = self._splitBall(ball)[1] return package['ver'] def getInstalledVersion(self): return self._splitBall(self.__installed[0][self.__pkgName])[1] def version(self): """print installed version""" if self.__pkgName: if self.__pkgName not in self.__installed[0]: msg = "{0} is not installed".format(self.__pkgName) raise PackageException(msg) print(self._versionToString(self.getInstalledVersion())) else: for self.__pkgName in self._psort(list( self.__installed[0].keys())): if self.__pkgName not in self.__installed[0]: self.__rc.distname = 'installed' raise PackageException(self._noPackage()) print("{0:<20}{1:<12}".format( self.__pkgName, self._versionToString(self.getInstalledVersion()))) def getNew(self): lst = [] for self.__pkgName in list(self.__installed[0].keys()): new = self.getVersion() ins = self.getInstalledVersion() if new > ins: self._debug(" {0} > {1}".format(new, ins)) lst.append(self.__pkgName) return lst def new(self): """list new (upgradable) packages in distribution""" for self.__pkgName in self._psort(self.getNew()): print("{0:<20}{1:<12}".format( self.__pkgName, self._versionToString(self.getVersion()))) def getMd5(self): url, md5 = self._getUrl() f = open(os.path.join(self.__downloadDir, url), 'rb') data = f.read() f.close() m = hashlib.md5() if 64 == len(md5): m = hashlib.sha256() if 128 == len(md5): m = hashlib.sha512() m.update(data) digest = m.hexdigest() return digest def _checkMd5(self): return self._getUrl()[1] == self.getMd5() def checksum(self): """check digest of cached package against database""" if not os.path.exists(self.getBall()): msg = "{0} not downloaded.".format(self.__pkgName) raise PackageCacheException(msg) url, md5 = self._getUrl() ball = os.path.basename(url) print("{0} {1}".format(md5, ball)) actual_md5 = self.getMd5() print("{0} {1}".format(actual_md5, ball)) if actual_md5 != md5: raise HashException( "digest of cached package doesn't match digest " "in setup.ini from mirror") def search(self): """search all package descriptions for string""" if not self.__pkgName: raise CommandLineException( "search command requires a string to search for") if not self.__regexSearch: regexp = re.escape(self.__pkgName) else: regexp = self.__pkgName packages = [] keys = [] if self.__rc.distname in self.__dists: keys = list(self.__dists[self.__rc.distname].keys()) else: for i in list(self.__dists.keys()): for j in list(self.__dists[i].keys()): if not j in keys: keys.append(j) for i in keys: self.__pkgName = i if not regexp or re.search(regexp, i) \ or re.search(regexp, self.getField('sdesc'), re.IGNORECASE) \ or re.search(regexp, self.getField('ldesc'), re.IGNORECASE): if self.__rc.distname in self.__dists: if self.__ballTarget in self.__dists[ self.__rc.distname][i]: packages.append(i) else: packages.append(i) for self.__pkgName in self._psort(packages): s = self.__pkgName d = self.getField('sdesc') if d: s += " - {0}".format(d[1:-1]) print(s) def show(self): """print package description""" s = self.__pkgName d = self.getField('sdesc') if d: s += ' - {0}'.format(d[1:-1]) ldesc = self.getField('ldesc') if ldesc != "": print(s + "\n") print(ldesc) else: print("{0}: not found in setup.ini: {1}".format(self.__appName, s)) # return an array with all packages that must to be install def getMissing(self): reqs = self.getRequires() missingreqs = [] # List of missing package on requires list for i in reqs: if i not in self.__installed[0]: missingreqs.append(i) if self.__pkgName not in self.__installed[0]: missingreqs.append(self.__pkgName) if missingreqs and self.__pkgName not in missingreqs: sys.stderr.write("warning: missing packages: {0}\n".format( " ".join(missingreqs))) elif self.__pkgName in self.__installed[0]: # Check version ins = self.getInstalledVersion() new = self.getVersion() if ins >= new: sys.stderr.write("{0} is already the newest version\n".format( self.__pkgName)) # missingreqs.remove(self.__pkgName) elif self.__pkgName not in missingreqs: missingreqs.append(self.__pkgName) return missingreqs def missing(self): """print missing dependencies for package""" missing = self.getMissing() if len(missing) > 0: print("\n".join(missing)) else: print("All dependent packages for {0} installed".format( self.__pkgName)) def _runScript(self, file_name, optional=True): mapped_file = self.__pm.mapPath(file_name) mapped_file_done = mapped_file + ".done" if os.path.isfile(mapped_file): sys.stderr.write("running: {0}\n".format(file_name)) cmd = ["bash"] + self.SH_OPTIONS + [mapped_file] if not self.__cygwinPlatform: cmd[0] = self.__dosBash cwd = None extension = os.path.splitext(mapped_file)[1] if ".dash" == extension: cmd = ["dash"] + self.DASH_OPTIONS + [mapped_file] if not self.__cygwinPlatform: cmd[0] = self.__dosDash if extension in [".bat", ".cmd"]: cmd = ["cmd" ] + self.CMD_OPTIONS + [os.path.basename(mapped_file)] cwd = os.path.dirname(mapped_file) retval = Process(cmd, cwd).run(True) if os.path.exists(mapped_file_done): os.remove(mapped_file_done) if retval == 0 and os.path.basename(file_name)[:3] not in [ '0p_', 'zp_' ]: shutil.move(mapped_file, mapped_file_done) else: if not optional: sys.stderr.write("{0}: WARNING couldn't find {1}.\n".format( self.__appName, mapped_file)) def _runAll(self, dirname): dirname = self.__pm.mapPath(dirname) if os.path.isdir(dirname): lst = list() for filename in os.listdir(dirname): if os.path.splitext(filename)[1] in [ '.sh', '.dash', '.bat', '.cmd' ]: lst.append(filename) perpetualScripts = list() regularScripts = list() for filename in lst: if filename[:3] in ['0p_', 'zp_']: perpetualScripts.append(filename) else: regularScripts.append(filename) perpetualScripts.sort() lst = perpetualScripts + regularScripts for i in lst: self._runScript("{0}/{1}".format(dirname, i)) def _doInstallExternal(self, ball): # Currently we use a temporary directory and extractall() then copy: # this is very slow. The Python documentation warns more sophisticated # approaches have pitfalls without specifying what they are. tf = cautils.open_tarfile(ball, self.__dosXz) members = tf.getmembers() tempdir = tempfile.mkdtemp() try: tf.extractall(tempdir) for m in members: if m.isdir(): path = self.__pm.mapPath("/" + m.name) if not os.path.exists(path): os.makedirs(path, m.mode) for m in members: if m.isdir(): path = self.__pm.mapPath("/" + m.name) if not os.path.exists(path): os.makedirs(path, m.mode) else: path = self.__pm.mapPath("/" + m.name) dirname = os.path.dirname(path) if not os.path.exists(dirname): os.makedirs(dirname) if os.path.exists(path): os.chmod(path, 0o777) os.remove(path) # Windows extract() is robust but can't form Cygwin links # (It produces copies instead: bulky and bugbait.) # Convert to links if possible -- depends on coreutils being installed if m.issym() and self.__lnExists: link_target = m.linkname Process([self.__dosLn, "-s", link_target, path]).run(True) elif m.islnk() and self.__lnExists: # Hard link -- expect these to be very rare link_target = m.linkname mapped_target = self.__pm.mapPath("/" + m.linkname) # Must ensure target exists before forming hard link if not os.path.exists(mapped_target): shutil.move(os.path.join(tempdir, link_target), mapped_target) Process([self.__dosLn, mapped_target, path]).run(True) else: shutil.move(os.path.join(tempdir, m.name), path) finally: tf.close() cautils.rmtree(tempdir) def _doInstall(self): ball = self.getBall() if cautils.is_tarfile(ball): if not self.__cygwinPlatform: self._doInstallExternal(ball) tf = cautils.open_tarfile(ball, self.__dosXz) if self.__cygwinPlatform: tf.extractall(self.__absRoot) # Force slash to the end of each directories members = tf.getmembers() tf.close() lst = [] for m in members: if m.isdir() and not m.name.endswith("/"): lst.append(m.name + "/") else: lst.append(m.name) else: print("{0}: bad tarball {1}. Install failed.".format( self.__appName, ball), file=sys.stderr) return self._writeFileList(lst) status = 1 if not self.__pkgName in self._integrityControl(): status = 0 self.__installed[status][self.__pkgName] = os.path.basename(ball) self._writeInstalled() def getFileList(self): filelist_file = os.path.join(self.__setupDir, "{0}.lst.gz".format(self.__pkgName)) if not os.path.exists(filelist_file): if self.__pkgName not in self.__installed[0]: raise PackageException("{0} is not installed".format( self.__pkgName)) else: raise PackageException( "{0} is installed, but {1} is missing".format( self.__pkgName, filelist_file)) gzf = gzip.GzipFile(filelist_file) lst = gzf.readlines() gzf.close() lst = [x.decode().strip() for x in lst] return lst def _touch(self, fname, times=None): f = open(fname, 'a') os.utime(fname, times) f.close() def _writeFileList(self, lst): gz_filename = os.path.join(self.__setupDir, "{0}.lst.gz".format(self.__pkgName)) lst_cr = [x + "\n" for x in lst] # create iostring and write in gzip lst_io = io.BytesIO() lst_io_gz = gzip.GzipFile(fileobj=lst_io, mode='w') lst_io_gz.writelines([x.encode() for x in lst_cr]) lst_io_gz.close() # save it in the file lst_gz = open(gz_filename, 'wb') lst_gz.write(lst_io.getvalue()) lst_gz.close() lst_io.close() stat_struct = os.stat(self.__setupIniPath) atime = stat_struct[7] mtime = stat_struct[8] self._touch(gz_filename, (atime, mtime)) def _removeFileList(self): lst_name = os.path.join(self.__setupDir, "{0}.lst.gz".format(self.__pkgName)) if os.path.exists(lst_name): os.remove(lst_name) else: sys.stderr.write("{0}: warning {1} no such file\n".format( sys.argv[0], lst_name)) def _uninstallWantFileRemoved(self, filename, noremoves, nowarns): # Returns true if the path from the tarball should result in a file # removal operation, false if not. if not os.path.exists(filename) and not os.path.islink(filename): if filename not in nowarns: sys.stderr.write( "warning: {0} no such file\n".format(filename)) return False elif not os.path.isdir(filename) and filename not in noremoves: return True def _doUninstall(self): postremove_sh = "{0}/{1}.sh".format(self.CYG_POSTREMOVE_DIR, self.__pkgName) postinstall_sh = "{0}/{1}.sh".format(self.CYG_POSTINSTALL_DIR, self.__pkgName) preremove_sh = "{0}/{1}.sh".format(self.CYG_PREREMOVE_DIR, self.__pkgName) postinstall_done = "{0}.done".format(postinstall_sh) suppression_msg = ("{0}: postremove suppressed: " "\"{0} postremove {1}\" to complete.".format( self.__appName, self.__pkgName)) lst = self.getFileList() expect_preremove = preremove_sh[1:] in lst expect_postremove = postremove_sh[1:] in lst if not self.__noPostRemove: if expect_preremove: self._runScript(preremove_sh, optional=False) else: print("{0}".format(suppression_msg), file=sys.stderr) # We don't expect these to be present: they are executed # and moved to $(packagename).sh.done nowarns = [] nowarns.append(self.__pm.mapPath(postinstall_sh)) nowarns.append(self.__pm.mapPath(preremove_sh)) noremoves = [] if self.__noPostRemove: noremoves.append(self.__pm.mapPath(preremove_sh)) noremoves.append(self.__pm.mapPath(postremove_sh)) # remove files for i in lst: filename = self.__pm.mapPath("/" + i) if os.path.islink(filename): os.remove(filename) continue if os.path.isdir(filename): continue if (self._uninstallWantFileRemoved(filename, noremoves, nowarns)): if os.path.exists(filename): if self.__cygwinPlatform: os.chmod(filename, 0o777) if os.remove(filename): raise IOError(filename) else: if os.path.islink(filename): os.remove(filename) else: print("{0}: warning: expected to remove {1} but it was" " not there.".format(self.__appName, filename)) if not self.__noPostRemove: if expect_postremove: self._runScript(postremove_sh, optional=False) postremove_sh_mapped = self.__pm.mapPath(postremove_sh) if os.path.isfile(postremove_sh_mapped): if os.remove(postremove_sh_mapped): raise IOError(postremove_sh_mapped) # We don't remove empty directories: the problem is are we sure no other # package is depending on them. # setup.exe removes the filelist when a package is uninstalled: we try to be # as much like setup.exe as possible self._removeFileList() # Clean up the postintall script: it's been moved to .done if os.path.isfile(self.__pm.mapPath(postinstall_done)): os.remove(self.__pm.mapPath(postinstall_done)) # update installed[] del (self.__installed[0][self.__pkgName]) self._writeInstalled() def remove(self): """uninstall packages""" barred = [] for self.__pkgName in self.__files[1:]: if self.__pkgName not in self.__installed[0]: sys.stderr.write("warning: {0} not installed\n".format( self.__pkgName)) continue if self._isBarredPackage(self.__pkgName): barred.append(self.__pkgName) continue sys.stderr.write("uninstalling {0} {1}\n".format( self.__pkgName, self._versionToString(self.getInstalledVersion()))) self._doUninstall() self._barredWarnIfNeed(barred, "removing") def purge(self): """uninstall packages and delete from cache""" barred = [] for self.__pkgName in self.__files[1:]: if self.__pkgName in self.__installed[0]: if self._isBarredPackage(self.__pkgName): barred.append(self.__pkgName) continue sys.stderr.write("uninstalling {0} {1}\n".format( self.__pkgName, self._versionToString(self.getInstalledVersion()))) self._doUninstall() scripts = [ self.CYG_POSTINSTALL_DIR + "/{0}.sh.done", self.CYG_PREREMOVE_DIR + "/{0}.sh.done", self.CYG_POSTREMOVE_DIR + "/{0}.sh.done" ] scripts = [s.format(self.__pkgName) for s in scripts] scripts = [self.__pm.mapPath(s) for s in scripts] for s in scripts: if os.path.exists(s): os.remove(s) ball = self.getBall() if os.path.exists(ball): print("{0}: removing {1}".format(self.__appName, ball)) os.remove(ball) self._barredWarnIfNeed(barred, "purging") def _barredWarnIfNeed(self, barred, command): num_barred = len(barred) if num_barred > 0: if num_barred == 1: this_these = "this package" else: this_these = "these packages" barredstr = ", ".join(barred) helpfull = "" close_all_cygwin_programs = "" if command == "installing": helpfull += ( "You can force the installation with the option -f. But it is recommended\n" "to upgrade the Cygwin distribution, with the official Setup program\n" "(e.g., setup.exe).") if "_autorebase" in barred: close_all_cygwin_programs += ( "\n" "Before that, you must close all Cygwin programs to perform rebasing\n" "(e.g., rebaseall).") print("BarredWarning: NOT {1}:" " {2}\n{0} is dependent on {3} under Cygwin." "{4}{5}".format(self.__appName, command, barredstr, this_these, helpfull, close_all_cygwin_programs), file=sys.stderr) if not self.__cygwinPlatform: print("Use -f to override but proceed with caution.", file=sys.stderr) def install(self): """download and install packages with dependencies""" suppression_msg = ("{0}: postinstall suppressed: " "\"{0} postinstall\" to complete.".format( self.__appName)) missing = {} barred = [] for self.__pkgName in self.__files[1:]: missing.update(dict([(x, 0) for x in self.getMissing()])) if len(missing) > 1: sys.stderr.write("to install: \n") sys.stderr.write(" {0}".format(" ".join(list(missing.keys())))) sys.stderr.write("\n") for self.__pkgName in list(missing.keys()): if not self._getUrl(): del missing[self.__pkgName] for self.__pkgName in list(missing.keys()): if self._isBarredPackage(self.__pkgName): barred.append(self.__pkgName) del missing[self.__pkgName] for self.__pkgName in list(missing.keys()): self.download() if self.__downloadOnly: return for self.__pkgName in list(missing.keys()): if self.__pkgName in self.__installed[0]: sys.stderr.write("preparing to replace {0} {1}\n".format( self.__pkgName, self._versionToString(self.getInstalledVersion()))) self._doUninstall() sys.stderr.write("installing {0} {1}\n".format( self.__pkgName, self._versionToString(self.getVersion()))) self._doInstall() if self.__noPostInstall: print(suppression_msg, file=sys.stderr) else: self._postInstall() self._barredWarnIfNeed(barred, "installing") def postinstall(self): """Executes all undone postinstall scripts.""" self._postInstall() def postremove(self): """Executes all undone preremove and postremove scripts.""" self._postRemove() def _integrityControl(self, checklist=None): if None is checklist: checklist = list() options = ["-c"] if self.__verbose: options.append("-v") if len(checklist) == 0: checklist.append(self.__pkgName) command = ["/bin/cygcheck"] + options + checklist if not self.__cygwinPlatform: command = subprocess.list2cmdline(command) command = [self.__dosBash] + self.SH_OPTIONS + ["-c"] + [command] p = Process(command) p.run() outputlines = p.getOutput().splitlines(True) unformat = "" start = False incomplete = [] for res in outputlines: try: res_split = res.split() package, version, status = res_split except ValueError: if len(res_split) > 0: unformat += res continue if package == 'Package' \ and version == 'Version' \ and status == 'Status': start = True unformat = '' elif not start: continue if start and status == 'Incomplete': print(res[:-2]) print(unformat) unformat = "" incomplete.append(package) return incomplete def upgrade(self): """all installed packages""" self.__files[1:] = self.getNew() self.install() def _printErr(self, err): print("{0}: {1}".format(self.__appName, err), file=sys.stderr) def _doUnpack(self): ball = self.getBall() basename = os.path.basename(ball) self.__pkgName = re.sub(r"(-src)*\.tar\.(bz2|gz)", '', basename) if os.path.exists(self.__pkgName): self._printErr("{0} already exists. Not overwriting.".format( self.__pkgName)) return 1 os.mkdir(self.__pkgName) if cautils.is_tarfile(ball): tf = cautils.open_tarfile(ball, self.__dosXz) tf.extractall(self.__pkgName) tf.close() else: raise InvalidFileException("Bad source tarball {0}, \n" "SOURCE UNPACK FAILED".format(ball)) if not os.path.exists(self.__pkgName): raise IOError(self.__pkgName) print(self.__pkgName) def source(self): """download source package""" self.__ballTarget = 'source' for self.__pkgName in self.__files[1:]: self.download() self._doUnpack() def find(self): """find package containing file""" if self.__regexSearch: file_to_find = self.__pkgName else: file_to_find = re.escape(self.__pkgName) hits = [] for self.__pkgName in self._psort(list(self.__installed[0].keys())): filenames_file = os.path.join(self.__setupDir, "{0}.lst.gz".format(self.__pkgName)) if not os.path.exists(filenames_file): continue files = self.getFileList() for i in files: if re.search(file_to_find, "/{0}".format(i)): hits.append("{0}: /{1}".format(self.__pkgName, i)) print("\n".join(hits)) def setRoot(self, root): if len(root) < 1 or root[-1] != "/": raise InvalidArgumentException("ROOT must end in a slash.") self.__prefixRoot = root[:-1] self.__absRoot = root def getRessource(self, filename): self.__rc = cautils.parse_rc(filename) if not self.__rc.cache: msg = "{0} doesn't define cache.".format(self.__rcFile) raise UnexpectedValueException(msg) if not self.__rc.mirror: msg = "{0} doesn't define mirror.".format(self.__rcFile) raise UnexpectedValueException(msg) # We want ROOT + "/etc/setup" and cd(ROOT) to work: # necessitates two different forms, prefix and absolute if (self.__cygwinPlatform): self.setRoot("/") else: self.setRoot(self.__rc.ROOT) self.__rc.ROOT = None self.__pm = PathMapper(self.__prefixRoot, self.__cygwinPlatform) self.__setupDir = self.__pm.mapPath(self.__setupDir) self.__rc.cache = self.__pm.mapPath(self.__rc.cache) self.__downloadDir = os.path.join( self.__rc.cache, urllib.quote( self.__rc.mirror + ('' if self.__rc.mirror.endswith('/') else '/'), '').lower()) self.__installedDbFile = os.path.join(self.__setupDir, "installed.db") self.__setupIniPath = os.path.join( self.__downloadDir, self.__arch, "setup.ini", ) self.__dosBash = "{0}bin/bash".format(self.__pm.getMountRoot()) self.__dosLn = "{0}bin/ln".format(self.__pm.getMountRoot()) self.__dosXz = self.__pm.mapPath("/usr/bin/xz") self.__dosDash = "{0}bin/dash".format(self.__pm.getMountRoot()) return 0 def _isBarredPackage(self, package): barred = [] # add user barred package barred.extend(self.__rc.barred.split()) # add force barred package barred.extend(self.FORCE_BARRED) # store current package name curr_pkgname = self.__pkgName # get barred package requires depbarred = [] for self.__pkgName in barred: try: depbarred.extend(self.getRequires()) except PackageException: pass barred.extend(depbarred) # set current package name self.__pkgName = curr_pkgname return (not self.__noBarred) and package in barred
class CygApt: INSTALLED_DB_MAGIC = "INSTALLED.DB 2\n"; DIST_NAMES = ('curr', 'test', 'prev'); FORCE_BARRED = ["python", "python-argparse", "gnupg", "xz"]; SH_OPTIONS = ["--norc", "--noprofile"]; DASH_OPTIONS = []; CMD_OPTIONS = ["/V:ON", "/E:ON", "/C"]; CYG_POSTINSTALL_DIR = "/etc/postinstall"; CYG_PREREMOVE_DIR = "/etc/preremove"; CYG_POSTREMOVE_DIR = "/etc/postremove"; def __init__(self, main_packagename, main_files, main_cyg_apt_rc, main_cygwin_p, main_download_p, main_mirror, main_downloads, main_distname, main_nodeps_p, main_regex_search, main_nobarred, main_nopostinstall, main_nopostremove, main_dists, main_installed, main_scriptname, main_verbose, arch, setupDir="/etc/setup", ): # Define private properties self.__ballTarget = 'install'; self.__regexSearch = main_regex_search; self.__noBarred = main_nobarred; self.__noPostInstall = main_nopostinstall; self.__noPostRemove = main_nopostremove; self.__appName = main_scriptname; self.__files = main_files; self.__downloadOnly = main_download_p; self.__downloadDir = main_downloads; self.__noDeps = main_nodeps_p; self.__rcFile = main_cyg_apt_rc; self.__verbose = main_verbose; self.__setupDir = setupDir; self.__rc = ConfigStructure(); self.__setupIniPath = None; self.__arch = arch; # Init self.setPkgName(main_packagename); self.setCygwinPlatform(main_cygwin_p); self.setDists(main_dists); self.setInstalled(main_installed); # Read in our configuration self.getRessource(self.__rcFile); # Now we have a path mapper, check setup.exe is not running self._checkForSetupExe(); # DOS specific if not self.__cygwinPlatform: self.__lnExists = os.path.exists( "{0}/bin/ln.exe".format(self.__prefixRoot) ); else: self.__lnExists = True; # Overrides to the .rc if (main_mirror): self.__rc.mirror = main_mirror; self.__downloadDir = os.path.join( self.__rc.cache, urllib.quote(self.__rc.mirror+('' if self.__rc.mirror.endswith('/') else '/'), '').lower() ); if (main_distname): self.__rc.distname = main_distname; if not (os.path.isfile(self.__installedDbFile) \ or os.path.isfile(self.__setupIniPath)): msg = "{0} no such file, run {1} setup?".format( self.__installedDbFile, self.__appName ); raise PackageCacheException(msg); else: self._getSetupIni(); self.getInstalled(); def getPkgName(self): return self.__pkgName; def setPkgName(self, pkg_name): self.__pkgName = str(pkg_name); def getCygwinPlatform(self): return self.__cygwinPlatform; def setCygwinPlatform(self, cygwin_p): self.__cygwinPlatform = bool(cygwin_p); def getDownloadDir(self): return self.__downloadDir; def setDownlaodDir(self, download_dir): self.__downloadDir = str(download_dir); def getDists(self): return self.__dists; def setDists(self, dists): self.__dists = dists; def getSetupDir(self): return self.__setupDir; def setSetupDir(self, setup_dir): self.__setupDir = str(setup_dir); def getInstalledDbFile(self): return self.__installedDbFile; def setInstalledDbFile(self, installed_db_file): self.__installedDbFile = str(installed_db_file); def getDosBash(self): return self.__dosBash; def setDosBash(self, dos_bash): self.__dosBash = str(dos_bash); def getDosLn(self): return self.__dosLn; def setDosLn(self, dos_ln): self.__dosLn = str(dos_ln); def getDosXz(self): return self.__dosXz; def setDosXz(self, dos_xz): self.__dosXz = str(dos_xz); def getDosDash(self): return self.__dosDash; def setDosDash(self, dosDash): self.__dosDash = str(dosDash); def getPrefixRoot(self): return self.__prefixRoot; def setPrefixRoot(self, prefix_root): self.__prefixRoot = prefix_root; def getAbsRoot(self): return self.__absRoot; def setAbsRoot(self, absolute_root): self.__absRoot = absolute_root; def getPathMapper(self): return self.__pm; def setPathMapper(self, path_mapper): assert isinstance(path_mapper, PathMapper); self.__pm = path_mapper; def getRC(self): return self.__rc; def setRC(self, rc_structure): assert isinstance(rc_structure, ConfigStructure); self.__rc = rc_structure; def _checkForSetupExe(self): # It's far from bulletproof, but it's surprisingly hard to detect # setup.exe running since it doesn't lock any files. p = Process([self.__pm.mapPath("/bin/ps"), "-W"]); p.run(); psout = p.getOutput().splitlines(True); setup_re = re.compile(r"(?<![a-z0-9_ -])setup(|-1\.7|-x86|-x86_64)\.exe", re.IGNORECASE); for l in psout: m = setup_re.search(l); if m: raise AppConflictException( "Please close {0} while " "running {1}".format(m.group(0), self.__appName) ); def _versionToString(self, t): def try_itoa(x): if isinstance(x, int): return "{0:d}".format(x); return x return "{0}-{1}".format( ".".join(list(map(try_itoa, t[:-1]))), t[-1] ); def _stringToVersion(self, s): s = re.sub(r"([^0-9][^0-9]*)", " \\1 ", s); s = re.sub(r"[ _.-][ _.-]*", " ", s); def try_atoi(x): if re.match(r"^[0-9]*$", x): return int(x); return x return tuple(map(try_atoi, (s.split(' ')))); def _splitBall(self, p): m = re.match(r"^(.*)-([0-9].*-[0-9]+)(.tar.(bz2|xz))?$", p); if not m: print("splitBall: {0}".format(p)); return (p[:2], (0, 0)); t = (m.group(1), self._stringToVersion(m.group(2))); return t; def _joinBall(self, t): return "{0}-{1}".format(t[0], self._versionToString(t[1])); def _debug(self, s): s; def help(self,): """this help message""" pass; def _getSetupIni(self): if self.__dists: return; self.__dists = {'test': {}, 'curr': {}, 'prev' : {}}; f = open(self.__setupIniPath); contents = f.read(); f.close(); chunks = contents.split("\n\n@ "); for i in chunks[1:]: lines = i.split("\n"); name = lines[0].strip(); self._debug("package: {0}".format(name)); packages = self.__dists['curr']; records = {'sdesc': name}; j = 1; while j < len(lines) and lines[j].strip(): self._debug("raw: {0}".format(lines[j])); if lines[j][0] == '#': j = j + 1; continue; elif lines[j][0] == '[': self._debug('dist: {0}'.format(lines[j][1:5])); packages[name] = records.copy(); packages = self.__dists[lines[j][1:5]]; j = j + 1; continue; duo = lines[j].split(": ", 1); key = duo[0].strip(); value = duo[1].strip(); if value.find('"') != -1 \ and value.find('"', value.find('"') + 1) == -1: while True: j = j + 1; value += "\n" + lines[j]; if lines[j].find('"') != -1: break; records[key] = value; j = j + 1; packages[name] = records; def _getUrl(self): if self.__pkgName not in self.__dists[self.__rc.distname] \ or self.__ballTarget not in self.__dists[self.__rc.distname][self.__pkgName]: self._printErr(self._noPackage()); install = 0; for d in self.DIST_NAMES: if self.__pkgName in self.__dists[d] \ and self.__ballTarget in self.__dists[d][self.__pkgName]: install = self.__dists[d][self.__pkgName][self.__ballTarget]; print("warning: using [{0}]\n".format(d), file=sys.stderr); break; if not install: raise PackageException("{0} is not in {1}".format( self.__pkgName, self.__setupIniPath )); else: install = self.__dists[self.__rc.distname][self.__pkgName][self.__ballTarget]; filename, size, md5 = install.split(); return filename, md5; def url(self): """print tarball url""" if not self.__pkgName: raise CommandLineException("url command requires a package name"); print("{0}/{1}".format(self.__rc.mirror, self._getUrl()[0])); def getBall(self): url, md5 = self._getUrl(); return os.path.join(self.__downloadDir, url); def ball(self): """print tarball name""" print(self.getBall()); def _doDownload(self): url, md5 = self._getUrl(); directory = os.path.join(self.__downloadDir, os.path.split(url)[0]); if not os.path.exists(self.getBall()) or not self._checkMd5(): if not os.path.exists(directory): os.makedirs(directory); status = cautils.uri_get( directory, "{0}/{1}".format(self.__rc.mirror, url) ); if status: raise PackageCacheException( "didn't find {0} " "on mirror {1}: possible mismatch between setup.ini and " "mirror requiring {2} update?".format( self.__pkgName, self.__rc.mirror, self.__appName )); def download(self): """download package (only, do not install)""" self._doDownload(); self.ball(); self.checksum(); def _noPackage(self): return "{0} is not on mirror {1} in [{2}].".format( self.__pkgName, self.__rc.mirror, self.__rc.distname ); # return an array contents all dependencies of self.__pkgName def getRequires(self): # Looking for dependencies on curr not prev or test dist = self.__dists['curr']; if self.__pkgName not in self.__dists[self.__rc.distname]: raise PackageException(self._noPackage()); if self.__noDeps: return []; reqs = {self.__pkgName:0}; if self.__ballTarget == 'source' \ and 'external-source' in dist[self.__pkgName]: reqs[dist[self.__pkgName]['external-source']] = 0; n = 0; while len(reqs) > n: n = len(reqs); for i in list(reqs.keys()): if i not in dist: sys.stderr.write("error: {0} not in [{1}]\n".format( i, self.__rc.distname )); if i != self.__pkgName: del reqs[i]; continue; reqs[i] = '0'; p = dist[i]; if 'requires' not in p: continue; update_list = [(x, 0) for x in p['requires'].split()]; reqs.update(update_list); # Delete the ask package it is not require by it self (joke) reqs.pop(self.__pkgName); rlist = sorted(reqs.keys()); return rlist; def requires(self): """print requires: for package""" reqs = self.getRequires(); if len(reqs) == 0: print("No dependencies for package {0}".format(self.__pkgName)); else: print("\n".join(reqs)); def getInstalled(self): if self.__installed: return self.__installed; self.__installed = {0:{}}; f = open(self.__installedDbFile); lines = f.readlines(); f.close(); for i in lines[1:]: name, ball, status = i.split(); self.__installed[int(status)][name] = ball; return self.__installed; def setInstalled(self, installed): self.__installed = installed; def _writeInstalled(self): file_db = open(self.__installedDbFile, 'w'); file_db.write(self.INSTALLED_DB_MAGIC); lines = []; for x in list(self.__installed[0].keys()): lines.append("{0} {1} 0\n".format(x, self.__installed[0][x])); file_db.writelines(lines); if file_db.close(): raise IOError(self.__installedDbFile); def getField(self, field, default=''): for d in (self.__rc.distname,) + self.DIST_NAMES: if self.__pkgName in self.__dists[d] \ and field in self.__dists[d][self.__pkgName]: return self.__dists[d][self.__pkgName][field]; return default; def _psort(self, lst): lst.sort(); return lst; def _preverse(self, lst): lst.reverse(); return lst; def list(self): """list installed packages""" print("--- Installed packages ---"); for self.__pkgName in self._psort(list(self.__installed[0].keys())): ins = self.getInstalledVersion(); new = 0; if self.__pkgName in self.__dists[self.__rc.distname] \ and self.__ballTarget in self.__dists[self.__rc.distname][self.__pkgName]: new = self.getVersion(); s = "{0:<19} {1:<15}".format( self.__pkgName, self._versionToString(ins) ); if new and new != ins: s += "({0})".format(self._versionToString(new)); print(s); def filelist(self): """list files installed by given packages""" if not self.__pkgName: msg = "no package name given."; raise CommandLineException(msg); else: print("\n".join(self.getFileList())); def _postInstall(self): self._runAll(self.CYG_POSTINSTALL_DIR); def _postRemove(self): if len(self.__files[1:]) == 0: msg = "must specify package to run postremove."; raise CommandLineException(msg); else: for self.__pkgName in self.__files[1:]: preremove_sh = os.path.join( self.CYG_PREREMOVE_DIR, "{0}.sh".format(self.__pkgName) ); postremove_sh = os.path.join( self.CYG_POSTREMOVE_DIR, "{0}.sh".format(self.__pkgName) ); self._runScript(preremove_sh); self._runScript(postremove_sh); def getVersion(self): if self.__pkgName not in self.__dists[self.__rc.distname] \ or self.__ballTarget not in self.__dists[self.__rc.distname][self.__pkgName]: self._printErr(self._noPackage()); return (0, 0); package = self.__dists[self.__rc.distname][self.__pkgName]; if 'ver' not in package: filename = package[self.__ballTarget].split()[0]; ball = os.path.split(filename)[1]; package['ver'] = self._splitBall(ball)[1]; return package['ver']; def getInstalledVersion(self): return self._splitBall(self.__installed[0][self.__pkgName])[1]; def version(self): """print installed version""" if self.__pkgName: if self.__pkgName not in self.__installed[0]: msg = "{0} is not installed".format(self.__pkgName); raise PackageException(msg); print(self._versionToString(self.getInstalledVersion())); else: for self.__pkgName in self._psort(list(self.__installed[0].keys())): if self.__pkgName not in self.__installed[0]: self.__rc.distname = 'installed'; raise PackageException(self._noPackage()); print("{0:<20}{1:<12}".format( self.__pkgName, self._versionToString(self.getInstalledVersion()) )); def getNew(self): lst = []; for self.__pkgName in list(self.__installed[0].keys()): new = self.getVersion(); ins = self.getInstalledVersion(); if new > ins: self._debug(" {0} > {1}".format(new, ins)); lst.append(self.__pkgName); return lst; def new(self): """list new (upgradable) packages in distribution""" for self.__pkgName in self._psort(self.getNew()): print("{0:<20}{1:<12}".format( self.__pkgName, self._versionToString(self.getVersion()) )); def getMd5(self): url, md5 = self._getUrl(); f = open(os.path.join(self.__downloadDir, url), 'rb'); data = f.read(); f.close(); m = hashlib.md5(); if 64 == len(md5) : m = hashlib.sha256(); if 128 == len(md5) : m = hashlib.sha512(); m.update(data); digest = m.hexdigest(); return digest; def _checkMd5(self): return self._getUrl()[1] == self.getMd5(); def checksum(self): """check digest of cached package against database""" if not os.path.exists(self.getBall()): msg = "{0} not downloaded.".format(self.__pkgName); raise PackageCacheException(msg); url, md5 = self._getUrl(); ball = os.path.basename(url); print("{0} {1}".format(md5, ball)); actual_md5 = self.getMd5(); print("{0} {1}".format(actual_md5, ball)); if actual_md5 != md5: raise HashException( "digest of cached package doesn't match digest " "in setup.ini from mirror" ); def search(self): """search all package descriptions for string""" if not self.__pkgName: raise CommandLineException( "search command requires a string to search for" ); if not self.__regexSearch: regexp = re.escape(self.__pkgName); else: regexp = self.__pkgName; packages = []; keys = []; if self.__rc.distname in self.__dists: keys = list(self.__dists[self.__rc.distname].keys()); else: for i in list(self.__dists.keys()): for j in list(self.__dists[i].keys()): if not j in keys: keys.append(j); for i in keys: self.__pkgName = i; if not regexp or re.search(regexp, i) \ or re.search(regexp, self.getField('sdesc'), re.IGNORECASE) \ or re.search(regexp, self.getField('ldesc'), re.IGNORECASE): if self.__rc.distname in self.__dists: if self.__ballTarget in self.__dists[self.__rc.distname][i]: packages.append(i); else: packages.append(i); for self.__pkgName in self._psort(packages): s = self.__pkgName; d = self.getField('sdesc'); if d: s += " - {0}".format(d[1:-1]); print(s); def show(self): """print package description""" s = self.__pkgName; d = self.getField('sdesc'); if d: s += ' - {0}'.format(d[1:-1]); ldesc = self.getField('ldesc'); if ldesc != "": print(s + "\n"); print(ldesc); else: print("{0}: not found in setup.ini: {1}".format( self.__appName, s )); # return an array with all packages that must to be install def getMissing(self): reqs = self.getRequires(); missingreqs = []; # List of missing package on requires list for i in reqs: if i not in self.__installed[0]: missingreqs.append(i); if self.__pkgName not in self.__installed[0]: missingreqs.append(self.__pkgName); if missingreqs and self.__pkgName not in missingreqs: sys.stderr.write("warning: missing packages: {0}\n".format( " ".join(missingreqs) )); elif self.__pkgName in self.__installed[0]: # Check version ins = self.getInstalledVersion(); new = self.getVersion(); if ins >= new: sys.stderr.write("{0} is already the newest version\n".format( self.__pkgName )); # missingreqs.remove(self.__pkgName) elif self.__pkgName not in missingreqs: missingreqs.append(self.__pkgName); return missingreqs; def missing(self): """print missing dependencies for package""" missing = self.getMissing(); if len(missing) > 0: print("\n".join(missing)); else: print("All dependent packages for {0} installed".format( self.__pkgName )); def _runScript(self, file_name, optional=True): mapped_file = self.__pm.mapPath(file_name); mapped_file_done = mapped_file + ".done"; if os.path.isfile(mapped_file): sys.stderr.write("running: {0}\n".format(file_name)); cmd = ["bash"] + self.SH_OPTIONS + [mapped_file]; if not self.__cygwinPlatform: cmd[0] = self.__dosBash; cwd = None; extension = os.path.splitext(mapped_file)[1]; if ".dash" == extension : cmd = ["dash"] + self.DASH_OPTIONS + [mapped_file]; if not self.__cygwinPlatform: cmd[0] = self.__dosDash; if extension in [".bat", ".cmd"] : cmd = ["cmd"] + self.CMD_OPTIONS + [os.path.basename(mapped_file)]; cwd = os.path.dirname(mapped_file); retval = Process(cmd, cwd).run(True); if os.path.exists(mapped_file_done): os.remove(mapped_file_done); if retval == 0 and os.path.basename(file_name)[:3] not in ['0p_', 'zp_']: shutil.move(mapped_file, mapped_file_done); else: if not optional: sys.stderr.write("{0}: WARNING couldn't find {1}.\n".format( self.__appName, mapped_file )); def _runAll(self, dirname): dirname = self.__pm.mapPath(dirname); if os.path.isdir(dirname): lst = list(); for filename in os.listdir(dirname) : if os.path.splitext(filename)[1] in ['.sh', '.dash', '.bat', '.cmd'] : lst.append(filename); perpetualScripts = list(); regularScripts = list(); for filename in lst: if filename[:3] in ['0p_', 'zp_'] : perpetualScripts.append(filename); else: regularScripts.append(filename); perpetualScripts.sort(); lst = perpetualScripts + regularScripts; for i in lst: self._runScript("{0}/{1}".format(dirname, i)); def _doInstallExternal(self, ball): # Currently we use a temporary directory and extractall() then copy: # this is very slow. The Python documentation warns more sophisticated # approaches have pitfalls without specifying what they are. tf = cautils.open_tarfile(ball, self.__dosXz); members = tf.getmembers(); tempdir = tempfile.mkdtemp(); try: tf.extractall(tempdir); for m in members: if m.isdir(): path = self.__pm.mapPath("/" + m.name); if not os.path.exists(path): os.makedirs(path, m.mode); for m in members: if m.isdir(): path = self.__pm.mapPath("/" + m.name); if not os.path.exists(path): os.makedirs(path, m.mode); else: path = self.__pm.mapPath("/" + m.name); dirname = os.path.dirname(path); if not os.path.exists(dirname): os.makedirs(dirname); if os.path.exists(path): os.chmod(path, 0o777); os.remove(path); # Windows extract() is robust but can't form Cygwin links # (It produces copies instead: bulky and bugbait.) # Convert to links if possible -- depends on coreutils being installed if m.issym() and self.__lnExists: link_target = m.linkname; Process([ self.__dosLn, "-s", link_target, path ]).run(True); elif m.islnk() and self.__lnExists: # Hard link -- expect these to be very rare link_target = m.linkname; mapped_target = self.__pm.mapPath("/" + m.linkname); # Must ensure target exists before forming hard link if not os.path.exists(mapped_target): shutil.move( os.path.join(tempdir, link_target), mapped_target ); Process([ self.__dosLn, mapped_target, path ]).run(True); else: shutil.move(os.path.join(tempdir, m.name), path); finally: tf.close(); cautils.rmtree(tempdir); def _doInstall(self): ball = self.getBall(); if cautils.is_tarfile(ball): if not self.__cygwinPlatform: self._doInstallExternal(ball); tf = cautils.open_tarfile(ball, self.__dosXz); if self.__cygwinPlatform: tf.extractall(self.__absRoot); # Force slash to the end of each directories members = tf.getmembers(); tf.close(); lst = []; for m in members: if m.isdir() and not m.name.endswith("/"): lst.append(m.name + "/"); else: lst.append(m.name); else: print("{0}: bad tarball {1}. Install failed.".format( self.__appName, ball ), file=sys.stderr); return; self._writeFileList(lst); status = 1; if not self.__pkgName in self._integrityControl(): status = 0; self.__installed[status][self.__pkgName] = os.path.basename(ball); self._writeInstalled(); def getFileList(self): filelist_file = os.path.join( self.__setupDir, "{0}.lst.gz".format(self.__pkgName) ); if not os.path.exists(filelist_file): if self.__pkgName not in self.__installed[0]: raise PackageException( "{0} is not installed".format(self.__pkgName) ); else: raise PackageException( "{0} is installed, but {1} is missing".format( self.__pkgName, filelist_file )); gzf = gzip.GzipFile(filelist_file); lst = gzf.readlines(); gzf.close(); lst = [x.decode().strip() for x in lst]; return lst; def _touch(self, fname, times=None): f = open(fname, 'a'); os.utime(fname, times); f.close(); def _writeFileList(self, lst): gz_filename = os.path.join( self.__setupDir, "{0}.lst.gz".format(self.__pkgName) ); lst_cr = [x + "\n" for x in lst]; # create iostring and write in gzip lst_io = io.BytesIO(); lst_io_gz = gzip.GzipFile(fileobj=lst_io, mode='w'); lst_io_gz.writelines([x.encode() for x in lst_cr]); lst_io_gz.close(); # save it in the file lst_gz = open(gz_filename, 'wb'); lst_gz.write(lst_io.getvalue()); lst_gz.close(); lst_io.close(); stat_struct = os.stat(self.__setupIniPath); atime = stat_struct[7]; mtime = stat_struct[8]; self._touch(gz_filename, (atime, mtime)); def _removeFileList(self): lst_name = os.path.join( self.__setupDir, "{0}.lst.gz".format(self.__pkgName) ); if os.path.exists(lst_name): os.remove(lst_name); else: sys.stderr.write("{0}: warning {1} no such file\n".format( sys.argv[0], lst_name )); def _uninstallWantFileRemoved(self, filename, noremoves, nowarns): # Returns true if the path from the tarball should result in a file # removal operation, false if not. if not os.path.exists(filename) and not os.path.islink(filename): if filename not in nowarns: sys.stderr.write("warning: {0} no such file\n".format( filename )); return False; elif not os.path.isdir(filename) and filename not in noremoves: return True; def _doUninstall(self): postremove_sh = "{0}/{1}.sh".format( self.CYG_POSTREMOVE_DIR, self.__pkgName ); postinstall_sh = "{0}/{1}.sh".format( self.CYG_POSTINSTALL_DIR, self.__pkgName ); preremove_sh = "{0}/{1}.sh".format( self.CYG_PREREMOVE_DIR, self.__pkgName ); postinstall_done = "{0}.done".format(postinstall_sh); suppression_msg = ( "{0}: postremove suppressed: " "\"{0} postremove {1}\" to complete.".format( self.__appName, self.__pkgName )); lst = self.getFileList(); expect_preremove = preremove_sh[1:] in lst; expect_postremove = postremove_sh[1:] in lst; if not self.__noPostRemove: if expect_preremove: self._runScript(preremove_sh, optional=False); else: print("{0}".format(suppression_msg), file=sys.stderr); # We don't expect these to be present: they are executed # and moved to $(packagename).sh.done nowarns = []; nowarns.append(self.__pm.mapPath(postinstall_sh)); nowarns.append(self.__pm.mapPath(preremove_sh)); noremoves = []; if self.__noPostRemove: noremoves.append(self.__pm.mapPath(preremove_sh)); noremoves.append(self.__pm.mapPath(postremove_sh)); # remove files for i in lst: filename = self.__pm.mapPath("/" + i); if os.path.islink(filename): os.remove(filename); continue; if os.path.isdir(filename): continue; if (self._uninstallWantFileRemoved(filename, noremoves, nowarns)): if os.path.exists(filename): if self.__cygwinPlatform: os.chmod(filename, 0o777); if os.remove(filename): raise IOError(filename); else: if os.path.islink(filename): os.remove(filename); else: print( "{0}: warning: expected to remove {1} but it was" " not there.".format( self.__appName, filename )); if not self.__noPostRemove: if expect_postremove: self._runScript(postremove_sh, optional=False); postremove_sh_mapped = self.__pm.mapPath(postremove_sh) if os.path.isfile(postremove_sh_mapped): if os.remove(postremove_sh_mapped): raise IOError(postremove_sh_mapped); # We don't remove empty directories: the problem is are we sure no other # package is depending on them. # setup.exe removes the filelist when a package is uninstalled: we try to be # as much like setup.exe as possible self._removeFileList(); # Clean up the postintall script: it's been moved to .done if os.path.isfile(self.__pm.mapPath(postinstall_done)): os.remove(self.__pm.mapPath(postinstall_done)); # update installed[] del(self.__installed[0][self.__pkgName]); self._writeInstalled(); def remove(self): """uninstall packages""" barred = []; for self.__pkgName in self.__files[1:]: if self.__pkgName not in self.__installed[0]: sys.stderr.write("warning: {0} not installed\n".format( self.__pkgName )); continue; if self._isBarredPackage(self.__pkgName): barred.append(self.__pkgName); continue; sys.stderr.write("uninstalling {0} {1}\n".format( self.__pkgName, self._versionToString(self.getInstalledVersion()) )); self._doUninstall(); self._barredWarnIfNeed(barred, "removing"); def purge(self): """uninstall packages and delete from cache""" barred = []; for self.__pkgName in self.__files[1:]: if self.__pkgName in self.__installed[0]: if self._isBarredPackage(self.__pkgName): barred.append(self.__pkgName); continue; sys.stderr.write("uninstalling {0} {1}\n".format( self.__pkgName, self._versionToString(self.getInstalledVersion()) )); self._doUninstall(); scripts = [ self.CYG_POSTINSTALL_DIR + "/{0}.sh.done", self.CYG_PREREMOVE_DIR + "/{0}.sh.done", self.CYG_POSTREMOVE_DIR + "/{0}.sh.done" ]; scripts = [s.format(self.__pkgName) for s in scripts]; scripts = [self.__pm.mapPath(s) for s in scripts]; for s in scripts: if os.path.exists(s): os.remove(s); ball = self.getBall(); if os.path.exists(ball): print("{0}: removing {1}".format(self.__appName, ball)); os.remove(ball); self._barredWarnIfNeed(barred, "purging"); def _barredWarnIfNeed(self, barred, command): num_barred = len(barred); if num_barred > 0: if num_barred == 1: this_these = "this package"; else: this_these = "these packages"; barredstr = ", ".join(barred); helpfull = ""; close_all_cygwin_programs = ""; if command == "installing": helpfull += ( "You can force the installation with the option -f. But it is recommended\n" "to upgrade the Cygwin distribution, with the official Setup program\n" "(e.g., setup.exe)." ); if "_autorebase" in barred: close_all_cygwin_programs += ( "\n" "Before that, you must close all Cygwin programs to perform rebasing\n" "(e.g., rebaseall)." ); print( "BarredWarning: NOT {1}:" " {2}\n{0} is dependent on {3} under Cygwin." "{4}{5}".format( self.__appName, command, barredstr, this_these, helpfull, close_all_cygwin_programs ), file=sys.stderr); if not self.__cygwinPlatform: print( "Use -f to override but proceed with caution.", file=sys.stderr ); def install(self): """download and install packages with dependencies""" suppression_msg = ( "{0}: postinstall suppressed: " "\"{0} postinstall\" to complete.".format( self.__appName )); missing = {}; barred = []; for self.__pkgName in self.__files[1:]: missing.update(dict([(x, 0) for x in self.getMissing()])); if len(missing) > 1: sys.stderr.write("to install: \n"); sys.stderr.write(" {0}".format(" ".join(list(missing.keys())))); sys.stderr.write("\n"); for self.__pkgName in list(missing.keys()): if not self._getUrl(): del missing[self.__pkgName]; for self.__pkgName in list(missing.keys()): if self._isBarredPackage(self.__pkgName): barred.append(self.__pkgName); del missing[self.__pkgName]; for self.__pkgName in list(missing.keys()): self.download(); if self.__downloadOnly: return; for self.__pkgName in list(missing.keys()): if self.__pkgName in self.__installed[0]: sys.stderr.write("preparing to replace {0} {1}\n".format( self.__pkgName, self._versionToString(self.getInstalledVersion()) )); self._doUninstall(); sys.stderr.write("installing {0} {1}\n".format( self.__pkgName, self._versionToString(self.getVersion()) )); self._doInstall(); if self.__noPostInstall: print(suppression_msg, file=sys.stderr); else: self._postInstall(); self._barredWarnIfNeed(barred, "installing"); def postinstall(self): """Executes all undone postinstall scripts.""" self._postInstall(); def postremove(self): """Executes all undone preremove and postremove scripts.""" self._postRemove(); def _integrityControl(self, checklist=None): if None is checklist : checklist = list(); options = ["-c"]; if self.__verbose: options.append("-v"); if len(checklist) == 0: checklist.append(self.__pkgName); command = ["/bin/cygcheck"] + options + checklist; if not self.__cygwinPlatform: command = subprocess.list2cmdline(command); command = [self.__dosBash] + self.SH_OPTIONS + ["-c"] + [command]; p = Process(command); p.run(); outputlines = p.getOutput().splitlines(True); unformat = ""; start = False; incomplete = []; for res in outputlines: try: res_split = res.split(); package, version, status = res_split; except ValueError: if len(res_split) > 0: unformat += res; continue; if package == 'Package' \ and version == 'Version' \ and status == 'Status': start = True; unformat = ''; elif not start: continue; if start and status == 'Incomplete': print(res[:-2]); print(unformat); unformat = ""; incomplete.append(package); return incomplete; def upgrade(self): """all installed packages""" self.__files[1:] = self.getNew(); self.install(); def _printErr(self, err): print("{0}: {1}".format(self.__appName, err), file=sys.stderr); def _doUnpack(self): ball = self.getBall(); basename = os.path.basename(ball); self.__pkgName = re.sub(r"(-src)*\.tar\.(bz2|gz)", '', basename); if os.path.exists(self.__pkgName): self._printErr("{0} already exists. Not overwriting.".format( self.__pkgName )); return 1; os.mkdir(self.__pkgName); if cautils.is_tarfile(ball): tf = cautils.open_tarfile(ball, self.__dosXz); tf.extractall(self.__pkgName); tf.close(); else: raise InvalidFileException( "Bad source tarball {0}, \n" "SOURCE UNPACK FAILED".format(ball) ); if not os.path.exists(self.__pkgName): raise IOError(self.__pkgName); print(self.__pkgName); def source(self): """download source package""" self.__ballTarget = 'source'; for self.__pkgName in self.__files[1:]: self.download(); self._doUnpack(); def find(self): """find package containing file""" if self.__regexSearch: file_to_find = self.__pkgName; else: file_to_find = re.escape(self.__pkgName); hits = []; for self.__pkgName in self._psort(list(self.__installed[0].keys())): filenames_file = os.path.join( self.__setupDir, "{0}.lst.gz".format(self.__pkgName) ); if not os.path.exists(filenames_file): continue; files = self.getFileList(); for i in files: if re.search(file_to_find, "/{0}".format(i)): hits.append("{0}: /{1}".format(self.__pkgName, i)); print("\n".join(hits)); def setRoot(self, root): if len(root) < 1 or root[-1] != "/": raise InvalidArgumentException("ROOT must end in a slash."); self.__prefixRoot = root[:-1]; self.__absRoot = root; def getRessource(self, filename): self.__rc = cautils.parse_rc(filename); if not self.__rc.cache: msg = "{0} doesn't define cache.".format(self.__rcFile); raise UnexpectedValueException(msg); if not self.__rc.mirror: msg = "{0} doesn't define mirror.".format(self.__rcFile); raise UnexpectedValueException(msg); # We want ROOT + "/etc/setup" and cd(ROOT) to work: # necessitates two different forms, prefix and absolute if(self.__cygwinPlatform): self.setRoot("/"); else: self.setRoot(self.__rc.ROOT); self.__rc.ROOT = None; self.__pm = PathMapper(self.__prefixRoot, self.__cygwinPlatform); self.__setupDir = self.__pm.mapPath(self.__setupDir); self.__rc.cache = self.__pm.mapPath(self.__rc.cache); self.__downloadDir = os.path.join( self.__rc.cache, urllib.quote(self.__rc.mirror+('' if self.__rc.mirror.endswith('/') else '/'), '').lower() ); self.__installedDbFile = os.path.join(self.__setupDir, "installed.db"); self.__setupIniPath = os.path.join( self.__downloadDir, self.__arch, "setup.ini", ); self.__dosBash = "{0}bin/bash".format(self.__pm.getMountRoot()); self.__dosLn = "{0}bin/ln".format(self.__pm.getMountRoot()); self.__dosXz = self.__pm.mapPath("/usr/bin/xz"); self.__dosDash = "{0}bin/dash".format(self.__pm.getMountRoot()); return 0; def _isBarredPackage(self, package): barred = []; # add user barred package barred.extend(self.__rc.barred.split()); # add force barred package barred.extend(self.FORCE_BARRED); # store current package name curr_pkgname = self.__pkgName; # get barred package requires depbarred = []; for self.__pkgName in barred: try: depbarred.extend(self.getRequires()); except PackageException: pass; barred.extend(depbarred); # set current package name self.__pkgName = curr_pkgname; return (not self.__noBarred) and package in barred;
def _createCygApt(self): """Creates a CygApt instance. @return: CygApt """ cygapt = CygApt( self._var_packagename, self._var_files, self._file_user_config, self._var_cygwin_p, self._var_download_p, self._var_mirror, self._var_downloads, self._var_distname, self._var_nodeps_p, self._var_regex_search, self._var_nobarred, self._var_nopostinstall, self._var_nopostremove, self._var_dists, self._var_installed, self._var_exename, self._var_verbose, self._var_arch, self._dir_confsetup, ) # set attributes rc = ConfigStructure() rc.cache = self._dir_execache rc.distname = 'curr' # BC layer for `setup_ini` configuration field del rc.__dict__['setup_ini'] rc.ROOT = self._dir_mtroot rc.always_update = False rc.mirror = self._var_mirror cygapt.setRC(rc) cygapt.setInstalledDbFile(self._file_installed_db) self.assertEqual(self._dir_confsetup, cygapt.getSetupDir()) pm = PathMapper("", False) pm.setRoot(self._dir_mtroot[:-1]) pm.setMountRoot(self._dir_mtroot) pm.setMap({self._dir_mtroot: self._dir_mtroot}) expected = self._dir_mtroot ret = pm.mapPath(self._dir_mtroot) self.assertEqual(ret, expected) expected = os.path.join(self._dir_mtroot, "diranme") ret = pm.mapPath(expected) self.assertEqual(ret, expected) cygapt.setPathMapper(pm) cygapt.setDists(self._var_setupIni.dists.__dict__) if self._var_cygwin_p: cygapt.CYG_POSTINSTALL_DIR = self._dir_postinstall cygapt.CYG_PREREMOVE_DIR = self._dir_preremove cygapt.CYG_POSTREMOVE_DIR = self._dir_postremove # requires bash, ln and xz on PATH cygapt.setDosBash("bash") cygapt.setDosLn("ln") cygapt.setDosXz('xz') cygapt.setDosDash('dash') cygapt.setPrefixRoot(self._dir_mtroot[:-1]) cygapt.setAbsRoot(self._dir_mtroot) cygapt.setInstalled({0: {}}) cygapt.FORCE_BARRED.extend([ self._var_setupIni.barredpkg.name, ]) return cygapt
def main(self): # parse command line arguments cap = CygAptArgParser(scriptname=self.getAppName()); args = cap.parse(); # initialize main variables with command line arguments and options main_command = args.command; main_files = args.package[:]; main_files.insert(0, main_command); main_packagename = None; if len(args.package) > 0: main_packagename = args.package[0]; main_verbose = args.verbose; main_download_p = args.download_p; main_mirror = args.mirror; main_distname = args.distname; main_noupdate = args.noupdate; main_nodeps_p = args.nodeps_p; main_regex_search = args.regex_search; main_nobarred = args.force; main_verify = args.verify; main_nopostinstall = args.nopostinstall; main_nopostremove = args.nopostremove; main_downloads = None; main_dists = 0; main_installed = 0; # locate and parse the configuration file main_cyg_apt_rc = self.getConfigPath(); config = None; if main_cyg_apt_rc: config = cautils.parse_rc(main_cyg_apt_rc); elif (main_command != "setup"): print( "{0}: no .{0}: run \"{0} setup\"".format(self.getAppName()), file=sys.stderr ); return 1; # create a CygAptSetup instance and its dependencies main_cygwin_p = (sys.platform == "cygwin"); is_64_bit = False; if main_cygwin_p : # Running Cygwin python, so python architecture == Cygwin architecture if 2**32 < sys.maxsize : is_64_bit = True; elif config and main_command != 'setup' : # Running Windows python, so examine cygwin1.dll pathMapper = PathMapper(config.ROOT.rstrip('\\/'), main_cygwin_p); if cautils.pe_is_64_bit(pathMapper.mapPath("/bin/cygwin1.dll")) : is_64_bit = True; if is_64_bit : main_arch = 'x86_64'; else: main_arch = 'x86'; cas = CygAptSetup(main_cygwin_p, main_verbose, main_arch); # run command if (main_command == "setup"): cas.setup(args.force); return 0; elif (main_command == "help"): cas.usage(main_cyg_apt_rc); return 0; elif (main_command == "update"): cas.update(main_cyg_apt_rc, main_verify, main_mirror=main_mirror); return 0; # make an update if needed update_not_needed = [ "ball", "find", "help", "purge", "remove", "version", "filelist", "update", "setup", "md5", ]; always_update = config.always_update; always_update = always_update and\ main_command not in update_not_needed and\ not main_noupdate; if always_update: cas.update(main_cyg_apt_rc, main_verify, main_mirror=main_mirror); if main_command and main_command in dir(CygApt): cyg_apt = CygApt( main_packagename, main_files, main_cyg_apt_rc, main_cygwin_p, main_download_p, main_mirror, main_downloads, main_distname, main_nodeps_p, main_regex_search, main_nobarred, main_nopostinstall, main_nopostremove, main_dists, main_installed, self.getAppName(), main_verbose, main_arch, ); getattr(cyg_apt, main_command)(); else: cas.usage(main_cyg_apt_rc); return 0;