From mboxrd@z Thu Jan  1 00:00:00 1970
Received: from pigeon.gentoo.org ([208.92.234.80] helo=lists.gentoo.org)
	by finch.gentoo.org with esmtp (Exim 4.60)
	(envelope-from <gentoo-commits+bounces-362095-garchives=archives.gentoo.org@lists.gentoo.org>)
	id 1Qgkn2-0002LD-10
	for garchives@archives.gentoo.org; Tue, 12 Jul 2011 21:46:48 +0000
Received: from pigeon.gentoo.org (localhost [127.0.0.1])
	by pigeon.gentoo.org (Postfix) with SMTP id CA14421C0AF;
	Tue, 12 Jul 2011 21:45:25 +0000 (UTC)
Received: from smtp.gentoo.org (smtp.gentoo.org [140.211.166.183])
	by pigeon.gentoo.org (Postfix) with ESMTP id 336A721C0AF
	for <gentoo-commits@lists.gentoo.org>; Tue, 12 Jul 2011 21:45:25 +0000 (UTC)
Received: from pelican.gentoo.org (unknown [66.219.59.40])
	(using TLSv1 with cipher ADH-AES256-SHA (256/256 bits))
	(No client certificate requested)
	by smtp.gentoo.org (Postfix) with ESMTPS id 8CD311B4086
	for <gentoo-commits@lists.gentoo.org>; Tue, 12 Jul 2011 21:45:24 +0000 (UTC)
Received: from localhost.localdomain (localhost [127.0.0.1])
	by pelican.gentoo.org (Postfix) with ESMTP id EBD058003F
	for <gentoo-commits@lists.gentoo.org>; Tue, 12 Jul 2011 21:45:23 +0000 (UTC)
From: "Paul Varner" <fuzzyray@gentoo.org>
To: gentoo-commits@lists.gentoo.org
Content-type: text/plain; charset=UTF-8
Reply-To: gentoo-dev@lists.gentoo.org, "Paul Varner" <fuzzyray@gentoo.org>
Message-ID: <9ccb3af94c0b2ffc12e8c9b149f9894af5fa81a4.fuzzyray@gentoo>
Subject: [gentoo-commits] proj/gentoolkit:gentoolkit commit in: pym/gentoolkit/revdep_rebuild/
X-VCS-Repository: proj/gentoolkit
X-VCS-Files: pym/gentoolkit/revdep_rebuild/revdep-rebuild.py
X-VCS-Directories: pym/gentoolkit/revdep_rebuild/
X-VCS-Committer: fuzzyray
X-VCS-Committer-Name: Paul Varner
X-VCS-Revision: 9ccb3af94c0b2ffc12e8c9b149f9894af5fa81a4
Date: Tue, 12 Jul 2011 21:45:23 +0000 (UTC)
Precedence: bulk
List-Post: <mailto:gentoo-commits@lists.gentoo.org>
List-Help: <mailto:gentoo-commits+help@lists.gentoo.org>
List-Unsubscribe: <mailto:gentoo-commits+unsubscribe@lists.gentoo.org>
List-Subscribe: <mailto:gentoo-commits+subscribe@lists.gentoo.org>
List-Id: Gentoo Linux mail <gentoo-commits.gentoo.org>
X-BeenThere: gentoo-commits@lists.gentoo.org
Content-Transfer-Encoding: quoted-printable
X-Archives-Salt: 
X-Archives-Hash: b8a2d503bff5ae4a9ce9ef941f7c6453

commit:     9ccb3af94c0b2ffc12e8c9b149f9894af5fa81a4
Author:     lis.slawek <lis.slawek <AT> 5234894e-44cd-11de-9a4c-a76526a19=
3c6>
AuthorDate: Mon Oct 25 09:24:47 2010 +0000
Commit:     Paul Varner <fuzzyray <AT> gentoo <DOT> org>
CommitDate: Tue Jul 12 21:29:00 2011 +0000
URL:        http://git.overlays.gentoo.org/gitweb/?p=3Dproj/gentoolkit.gi=
t;a=3Dcommit;h=3D9ccb3af9

Initial revision of revdep-rebuild python version

git-svn-id: http://genscripts.googlecode.com/svn/trunk/gentoolkit@471 523=
4894e-44cd-11de-9a4c-a76526a193c6

---
 pym/gentoolkit/revdep_rebuild/revdep-rebuild.py |  723 +++++++++++++++++=
++++++
 1 files changed, 723 insertions(+), 0 deletions(-)

diff --git a/pym/gentoolkit/revdep_rebuild/revdep-rebuild.py b/pym/gentoo=
lkit/revdep_rebuild/revdep-rebuild.py
new file mode 100755
index 0000000..23298f9
--- /dev/null
+++ b/pym/gentoolkit/revdep_rebuild/revdep-rebuild.py
@@ -0,0 +1,723 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+
+# Author: S=C5=82awomir Lis <lis.slawek@gmail.com>
+# revdep-rebuild original author: Stanislav Brabec
+# revdep-rebuild original rewrite Author: Michael A. Smith
+# Current Maintainer: Paul Varner <fuzzyray@gentoo.org>
+
+# Creation date: 2010/10/17
+# License: BSD
+
+import subprocess
+import os
+import sys
+import re
+import getopt
+import signal
+import stat
+import time
+import glob
+import portage
+from portage import portdb
+from portage.output import bold, red, blue, yellow, green, nocolor
+
+APP_NAME =3D sys.argv[0]
+VERSION =3D '0.1-r2'
+
+
+__productname__ =3D "revdep-ng"
+
+
+
+# configuration variables
+DEFAULT_LD_FILE =3D 'etc/ld.so.conf'
+DEFAULT_ENV_FILE =3D 'etc/profile.env'
+
+
+# global variables
+PRINT_DEBUG =3D False      #program in debug mode
+
+PRETEND =3D False     #pretend only
+EXACT =3D False      #exact package version
+USE_TMP_FILES =3D True #if program should use temporary files from previ=
ous run
+DEFAULT_TMP_DIR =3D '/tmp/revdep-rebuild' #cache default location
+VERBOSITY =3D 1      #verbosity level; 0-quiet, 1-norm., 2-verbose
+
+IS_DEV =3D True       #True for dev. version, False for stable
+NO_PRETEND =3D False  #used when IS_DEV is True, forces to call emerge f=
rom script
+
+
+
+# util. functions
+def call_program(args):
+    ''' Calls program with specified parameters and returns stdout '''
+    subp =3D subprocess.Popen(args, stdout=3Dsubprocess.PIPE, \
+                                stderr=3Dsubprocess.PIPE)
+    stdout, stderr =3D subp.communicate()
+    return stdout
+
+
+def print_v(verbosity, args):
+    """Prints to stdout according to the verbosity level=20
+    and the global VERBOSITY level
+   =20
+    @param verbosity: integer
+    @param args: string to print
+    """
+    if verbosity > VERBOSITY:
+        return
+    print args
+
+
+def exithandler(signum, frame):
+    print 'Signal catched!'
+    print 'Bye!'
+    signal.signal(signal.SIGINT, signal.SIG_IGN)
+    signal.signal(signal.SIGTERM, signal.SIG_IGN)
+    sys.exit(1)
+
+
+def print_usage():
+    print APP_NAME + ': (' + VERSION +')'
+    print
+    print 'This is free software; see the source for copying conditions.=
'
+    print
+    print 'Usage: ' + APP_NAME + ' [OPTIONS] [--] [EMERGE_OPTIONS]'
+    print
+    print 'Broken reverse dependency rebuilder, python implementation.'
+    print
+    print 'Available options:'
+    print '''
+  -C, --nocolor        Turn off colored output
+  -d, --debug          Print debug informations
+  -e, --exact          Emerge based on exact package version
+  -h, --help           Print this usage
+  -i, --ignore         Ignore temporary files from previous runs (also w=
on't create any)
+  -L, --library NAME   Emerge existing packages that use the library wit=
h NAME
+      --library=3DNAME   NAME can be a full path to the library or a bas=
ic
+                       regular expression (man grep)
+  -l, --no-ld-path     Do not set LD_LIBRARY_PATH
+  -o, --no-order       Do not check the build order
+                       (Saves time, but may cause breakage.)
+  -p, --pretend        Do a trial run without actually emerging anything
+                       (also passed to emerge command)
+  -q, --quiet          Be less verbose (also passed to emerge command)
+  -v, --verbose        Be more verbose (also passed to emerge command)
+'''
+    print 'Calls emerge, options after -- are ignored by ' + APP_NAME
+    print 'and passed directly to emerge.'
+
+
+
+# functions
+def parse_conf(conf_file=3DNone, visited=3DNone):
+    ''' Parses supplied conf_file for libraries pathes.
+        conf_file is file or files to parse
+        visited is set of files already parsed
+    '''
+
+    if conf_file is None:
+        conf_file =3D os.path.join(portage.root, DEFAULT_LD_FILE)
+
+    lib_dirs =3D set()
+    to_parse =3D set()
+
+    if isinstance(conf_file, basestring):
+        conf_file =3D [conf_file]
+
+    for conf in conf_file:
+        try:
+            with open(conf) as f:
+                for line in f.readlines():
+                    line =3D line.strip()
+                    if line.startswith('#'):
+                        continue
+                    elif line.startswith('include'):
+                        include_line =3D line.split()[1:]
+                        for included in include_line:
+                            if not included.startswith('/'):
+                                path =3D os.path.join(os.path.dirname(co=
nf), \
+                                                    included)
+                            else:
+                                path =3D included
+
+                            to_parse =3D to_parse.union(glob.glob(path))
+                    else:
+                        lib_dirs.add(line)
+        except EnvironmentError:
+            print_v(2, 'Error when parsing file %s' %conf)
+
+    if visited is None:
+        visited =3D set()
+
+    visited =3D visited.union(conf_file)
+    to_parse =3D to_parse.difference(visited)
+    if to_parse:
+        lib_dirs =3D lib_dirs.union(parse_conf(to_parse, visited))
+
+    return lib_dirs
+
+
+def prepare_search_dirs():
+    ''' Lookup for search dirs. Returns tuple with two lists,
+        (list_of_bin_dirs, list_of_lib_dirs)
+    '''
+
+    bin_dirs =3D set(['/bin', '/usr/bin', ])
+    lib_dirs =3D set(['/lib', '/usr/lib', ])
+
+    try:
+        with open(os.path.join(portage.root, DEFAULT_ENV_FILE), 'r') as =
f:
+            for line in f.readlines():
+                line =3D line.strip()
+                m =3D re.match("^export (ROOT)?PATH=3D'([^']+)'", line)
+                if m is not None:
+                    bin_dirs =3D bin_dirs.union(set(m.group(2).split(':'=
)))
+    except EnvironmentError:
+        print_v(2, 'Could not open file %s' % f)
+
+    lib_dirs =3D parse_conf()
+    return (bin_dirs, lib_dirs)
+
+
+def parse_revdep_config():
+    ''' Parses all files under /etc/revdep-rebuild/ and returns
+        tuple of: (masked_dirs, masked_files, search_dirs)'''
+
+    search_dirs =3D set()
+    masked_dirs =3D set()
+    masked_files =3D set()
+
+    for f in os.listdir('/etc/revdep-rebuild/'):
+        for line in open(os.path.join('/etc/revdep-rebuild', f)):
+            line =3D line.strip()
+            if not line.startswith('#'): #first check for comment, we do=
 not want to regex all lines
+                m =3D re.match('LD_LIBRARY_MASK=3D\\"([^"]+)\\"', line)
+                if m is not None:
+                    s =3D m.group(1).split(' ')
+                    masked_files =3D masked_files.union(s)
+                    continue
+                m =3D re.match('SEARCH_DIRS_MASK=3D\\"([^"]+)\\"', line)
+                if m is not None:
+                    s =3D m.group(1).split(' ')
+                    for ss in s:
+                        masked_dirs =3D masked_dirs.union(glob.glob(ss))
+                    continue
+                m =3D re.match('SEARCH_DIRS=3D\\"([^"]+)\\"', line)
+                if m is not None:
+                    s =3D m.group(1).split(' ')
+                    for ss in s:
+                        search_dirs =3D masked_dirs.union(glob.glob(ss))
+                    continue
+
+    return (masked_dirs, masked_files, search_dirs)
+
+
+def collect_libraries_from_dir(dirs, mask):
+    ''' Collects all libraries from specified list of directories.
+        mask is list of pathes, that are ommited in scanning, can be eig=
hter single file or entire directory
+        Returns tuple composed of: list of libraries, list of symlinks, =
and toupe with pair
+        (symlink_id, library_id) for resolving dependencies
+    '''
+
+
+    found_directories =3D []  # contains list of directories found; allo=
w us to reduce number of fnc calls
+    found_files =3D []
+    found_symlinks =3D []
+    found_la_files =3D [] # la libraries
+    symlink_pairs =3D []  # list of pairs symlink_id->library_id
+
+    for d in dirs:
+        if d in mask:
+            continue
+
+        try:
+            for l in os.listdir(d):
+                l =3D os.path.join(d, l)
+                if l in mask:
+                    continue
+
+                if os.path.isdir(l):
+                    if os.path.islink(l):
+                        #we do not want scan symlink-directories
+                        pass
+                    else:
+                        found_directories.append(l)
+                elif os.path.isfile(l):
+                    if l.endswith('.so') or '.so.' in l:
+                        if l in found_files or l in found_symlinks:
+                            continue
+
+                        if os.path.islink(l):
+                            found_symlinks.append(l)
+                            abs_path =3D os.path.realpath(l)
+                            if abs_path in found_files:
+                                i =3D found_files.index(abs_path)
+                            else:
+                                found_files.append(abs_path)
+                                i =3D len(found_files)-1
+                            symlink_pairs.append((len(found_symlinks)-1,=
 i,))
+                        else:
+                            found_files.append(l)
+                        continue
+                    elif l.endswith('.la'):
+                        if l in found_la_files:
+                            continue
+
+                        found_la_files.append(l)
+                    else:
+                        # sometimes there are binaries in libs' subdir, =
for example in nagios
+                        if not os.path.islink(l):
+                            if l in found_files or l in found_symlinks:
+                                continue
+                            prv =3D os.stat(l)[stat.ST_MODE]
+                            if prv & stat.S_IXUSR =3D=3D stat.S_IXUSR or=
 \
+                                    prv & stat.S_IXGRP =3D=3D stat.S_IXG=
RP or \
+                                    prv & stat.S_IXOTH =3D=3D stat.S_IXO=
TH:
+                                found_files.append(l)
+        except:
+            pass
+
+
+    if found_directories:
+        f,a,l,p =3D collect_libraries_from_dir(found_directories, mask)
+        found_files+=3Df
+        found_la_files+=3Da
+        found_symlinks+=3Dl
+        symlink_pairs+=3Dp
+
+    return (found_files, found_la_files, found_symlinks, symlink_pairs)
+
+
+def collect_binaries_from_dir(dirs, mask):
+    ''' Collects all binaries from specified list of directories.
+        mask is list of pathes, that are ommited in scanning, can be eig=
hter single file or entire directory
+        Returns list of binaries
+    '''
+
+    found_directories =3D []  # contains list of directories found; allo=
w us to reduce number of fnc calls
+    found_files =3D []
+
+    for d in dirs:
+        if d in mask:
+            continue
+
+        try:
+            for l in os.listdir(d):
+                l =3D os.path.join(d, l)
+                if d in mask:
+                    continue
+
+                if os.path.isdir(l):
+                    if os.path.islink(l):
+                        #we do not want scan symlink-directories
+                        pass
+                    else:
+                        found_directories.append(l)
+                elif os.path.isfile(l):
+                    #we're looking for binaries, and with binaries we do=
 not need links, thus we can optimize a bit
+                    if not os.path.islink(l):
+                        prv =3D os.stat(l)[stat.ST_MODE]
+                        if prv & stat.S_IXUSR =3D=3D stat.S_IXUSR or \
+                                prv & stat.S_IXGRP =3D=3D stat.S_IXGRP o=
r \
+                                prv & stat.S_IXOTH =3D=3D stat.S_IXOTH:
+                            found_files.append(l)
+        except:
+            pass
+
+    if found_directories:
+        found_files +=3D collect_binaries_from_dir(found_directories, ma=
sk)
+
+    return found_files
+
+
+def _match_str_in_list(lst, stri):
+    for l in lst:
+        if stri.endswith(l):
+            return l
+    return False
+
+
+def prepare_checks(files_to_check, libraries):
+    ''' Calls scanelf for all files_to_check, then returns found librari=
es and dependencies '''
+
+    libs =3D [] # libs found by scanelf
+    dependencies =3D [] # list of lists of files (from file_to_check) th=
at uses
+                      # library (for dependencies[id] and libs[id] =3D> =
id=3D=3Did)
+
+    for line in call_program(['scanelf', '-nBF', '%F %n',]+files_to_chec=
k).strip().split('\n'):
+        r =3D line.strip().split(' ')
+        if len(r) < 2: # no dependencies?
+            continue
+
+        deps =3D r[1].split(',')
+        for d in deps:
+            if d in libs:
+                i =3D libs.index(d)
+                dependencies[i].append(r[0])
+            else:
+                libs.append(d)
+                dependencies.append([r[0],])
+    return (libs, dependencies)
+
+
+def extract_dependencies_from_la(la, libraries, to_check):
+    broken =3D []
+    for f in la:
+        for line in open(f, 'r').readlines():
+            line =3D line.strip()
+            if line.startswith('dependency_libs=3D'):
+                m =3D re.match("dependency_libs=3D'([^']+)'", line)
+                if m is not None:
+                    for el in m.group(1).split(' '):
+                        el =3D el.strip()
+                        if len(el) < 1 or el.startswith('-'):
+                            continue
+
+                        if el in la or el in libraries:
+                            pass
+                        else:
+                            if to_check:
+                                _break =3D False
+                                for tc in to_check:
+                                    if tc in el:
+                                        _break =3D True
+                                        break
+                                if not _break:
+                                    continue
+
+                            print_v(1, yellow(' * ') + f + ' is broken (=
requires: ' + bold(el))
+                            broken.append(f)
+    return broken
+
+
+
+def find_broken(found_libs, system_libraries, to_check):
+    ''' Search for broken libraries.
+        Check if system_libraries contains found_libs, where
+        system_libraries is list of obsolute pathes and found_libs
+        is list of library names.
+    '''
+
+    # join libraries and looking at it as string is way too faster than =
for-jumping
+
+    broken =3D []
+    sl =3D '|'.join(system_libraries)
+
+    if not to_check:
+        for f in found_libs:
+            if f+'|' not in sl:
+                broken.append(found_libs.index(f))
+    else:
+        for tc in to_check:
+            for f in found_libs:
+                if tc in f and f+'|' not in sl:
+                    broken.append(found_libs.index(f))
+        #print broken
+
+    return broken
+
+
+def main_checks(found_libs, broken, dependencies):
+    ''' Checks for broken dependencies.
+        found_libs have to be the same as returned by prepare_checks
+        broken is list of libraries found by scanelf
+        dependencies is the value returned by prepare_checks
+    '''
+
+    broken_pathes =3D []
+
+    for b in broken:
+        f =3D found_libs[b]
+        print_v(1, 'Broken files that requires: ' + bold(f))
+        for d in dependencies[b]:
+            print_v(1, yellow(' * ') + d)
+            broken_pathes.append(d)
+    return broken_pathes
+
+
+def assign_packages(broken, output):
+    ''' Finds and returns packages that owns files placed in broken.
+        Broken is list of files
+    '''
+    assigned =3D set()
+    for group in os.listdir('/var/db/pkg'):
+        for pkg in os.listdir('/var/db/pkg/' + group):
+            f =3D '/var/db/pkg/' + group + '/' + pkg + '/CONTENTS'
+            if os.path.exists(f):
+                try:
+                    with open(f, 'r') as cnt:
+                        for line in cnt.readlines():
+                            m =3D re.match('^obj (/[^ ]+)', line)
+                            if m is not None:
+                                m =3D m.group(1)
+                                if m in broken:
+                                    found =3D group+'/'+pkg
+                                    if found not in assigned:
+                                        assigned.add(found)
+                                    print_v(1, '\t' + m + ' -> ' + bold(=
found))
+                except:
+                    output(1, red(' !! Failed to read ' + f))
+
+    return assigned
+
+
+def get_best_match(cpv, cp):
+    """Tries to find another version of the pkg with the same slot
+    as the deprecated installed version.  Failing that attempt to get an=
y version
+    of the same app
+   =20
+    @param cpv: string
+    @param cp: string
+    @rtype tuple: ([cpv,...], SLOT)
+    """
+
+    slot =3D portage.db[portage.root]["vartree"].dbapi.aux_get(cpv, ["SL=
OT"])
+    print_v(1, yellow('Warn: ebuild ' + cpv + ' not found.'))
+    print_v(1, 'Looking for %s:%s' %(cp, slot))
+    try:
+        m =3D portdb.match('%s:%s' %(cp, slot))
+    except portage.exception.InvalidAtom:
+        m =3D None
+
+    if not m:
+        print_v(1, red('Could not find ebuild for %s:%s' %(cp, slot)))
+        slot =3D ['']
+        m =3D portdb.match(cp)
+        if not m:
+            print_v(1, red('Could not find ebuild for ' + cp))
+    return m, slot
+
+
+def get_slotted_cps(cpvs):
+    """Uses portage to reduce the cpv list into a cp:slot list and retur=
ns it
+    """
+    from portage.versions import catpkgsplit
+    from portage import portdb
+
+    cps =3D []
+    for cpv in cpvs:
+        parts =3D catpkgsplit(cpv)
+        cp =3D parts[0] + '/' + parts[1]
+        try:
+            slot =3D portdb.aux_get(cpv, ["SLOT"])
+        except KeyError:
+            m, slot =3D get_best_match(cpv, cp)
+            if not m:
+                print_v(1, red("Installed package: %s is no longer avail=
able" %cp))
+                continue
+
+        if slot[0]:
+            cps.append(cp + ":" + slot[0])
+        else:
+            cps.append(cp)
+
+    return cps
+
+
+def read_cache(temp_path=3DDEFAULT_TMP_DIR):
+    ''' Reads cache information needed by analyse function.
+        This function does not checks if files exists nor timestamps,
+        check_temp_files should be called first
+        @param temp_path: directory where all temp files should reside
+        @return tuple with values of: libraries, la_libraries, libraries=
_links, symlink_pairs, binaries
+    '''
+
+    ret =3D {'libraries':[], 'la_libraries':[], 'libraries_links':[], 'b=
inaries':[]}
+    try:
+        for key,val in ret.iteritems():
+            f =3D open(os.path.join(temp_path, key))
+            for line in f.readlines():
+                val.append(line.strip())
+            #libraries.remove('\n')
+            f.close()
+    except EnvironmentError:
+        pass
+
+    return (ret['libraries'], ret['la_libraries'], ret['libraries_links'=
], ret['binaries'])
+
+
+def save_cache(to_save, temp_path=3DDEFAULT_TMP_DIR):
+    ''' Tries to store caching information.
+        @param to_save have to be dict with keys: libraries, la_librarie=
s, libraries_links and binaries
+    '''
+
+    if not os.path.exists(temp_path):
+        os.makedirs(temp_path)
+
+    f =3D open(os.path.join(temp_path, 'timestamp'), 'w')
+    f.write(str(int(time.time())))
+    f.close()
+
+    for key,val in to_save.iteritems():
+        f =3D open(os.path.join(temp_path, key), 'w')
+        for line in val:
+            f.write(line + '\n')
+        f.close()
+
+
+def analyse(output=3Dprint_v, libraries=3DNone, la_libraries=3DNone, lib=
raries_links=3DNone, binaries=3DNone):
+    """Main program body.  It will collect all info and determine the
+    pkgs needing rebuilding.
+
+    @param output: optional print/data gathering routine. Defaults to pr=
int_v
+            which prints to sys.stdout. Refer to print_v parameters for =
more detail.
+    @rtype list: list of pkgs that need rebuilding
+    """
+
+    if libraries and la_libraries and libraries_links and binaries:
+        output(1, blue(' * ') + bold('Found cache, skipping collecting p=
hase'))
+    else:
+        #TODO: add partial cache (for ex. only libraries) when found for=
 some reason
+
+        output(1, green(' * ') + bold('Collecting system binaries and li=
braries'))
+        bin_dirs, lib_dirs =3D prepare_search_dirs()
+
+        masked_dirs, masked_files, ld =3D parse_revdep_config()
+        lib_dirs =3D lib_dirs.union(ld)
+        bin_dirs =3D bin_dirs.union(ld)
+        masked_dirs =3D masked_dirs.union(set(['/lib/modules', '/lib32/m=
odules', '/lib64/modules',]))
+
+        output(1, green(' * ') + bold('Collecting dynamic linking inform=
ations'))
+        libraries, la_libraries, libraries_links, symlink_pairs =3D coll=
ect_libraries_from_dir(lib_dirs, masked_dirs)
+        binaries =3D collect_binaries_from_dir(bin_dirs, masked_dirs)
+
+        if USE_TMP_FILES:
+            save_cache(to_save=3D{'libraries':libraries, 'la_libraries':=
la_libraries, 'libraries_links':libraries_links, 'binaries':binaries})
+
+
+    output(2, 'Found '+ str(len(libraries)) + ' libraries (+' + str(len(=
libraries_links)) + ' symlinks) and ' + str(len(binaries)) + ' binaries')
+
+    output(1, green(' * ') + bold('Checking dynamic linking consistency'=
))
+    output(2,'Search for ' + str(len(binaries)+len(libraries)) + ' withi=
n ' + str(len(libraries)+len(libraries_links)))
+    libs_and_bins =3D libraries+binaries
+
+    found_libs, dependencies =3D prepare_checks(libs_and_bins, libraries=
+libraries_links)
+
+    broken =3D find_broken(found_libs, libraries+libraries_links, _libs_=
to_check)
+    broken_la =3D extract_dependencies_from_la(la_libraries, libraries+l=
ibraries_links, _libs_to_check)
+
+    broken_pathes =3D main_checks(found_libs, broken, dependencies)
+    broken_pathes +=3D broken_la
+
+    output(1, green(' * ') + bold('Assign files to packages'))
+
+    return assign_packages(broken_pathes, output)
+
+
+def check_temp_files(temp_path=3DDEFAULT_TMP_DIR, max_delay=3D3600):
+    ''' Checks if temporary files from previous run are still available
+        and if they aren't too old
+        @param temp_path is directory, where temporary files should be f=
ound
+        @param max_delay is maximum time difference (in seconds) when th=
ose files
+                are still considered fresh and useful
+        returns True, when files can be used, or False, when they don't
+        exists or they are too old
+    '''
+
+    if not os.path.exists(temp_path) or not os.path.isdir(temp_path):
+        return False
+
+    timestamp_path =3D os.path.join(temp_path, 'timestamp')
+    if not os.path.exists(timestamp_path) or not os.path.isfile(timestam=
p_path):
+        return False
+
+    try:
+        f =3D open(timestamp_path)
+        timestamp =3D int(f.readline())
+        f.close()
+    except:
+        timestamp =3D 0
+        return False
+
+    diff =3D int(time.time()) - timestamp
+    return max_delay > diff
+
+
+# Runs from here
+if __name__ =3D=3D "__main__":
+    _libs_to_check =3D set()
+
+    try:
+        opts, args =3D getopt.getopt(sys.argv[1:], 'dehiklopqvCL:P', ['n=
ocolor', 'debug', 'exact', 'help', 'ignore',\
+            'keep-temp', 'library=3D', 'no-ld-path', 'no-order', 'preten=
d', 'no-pretend', 'no-progress', 'quiet', 'verbose'])
+
+        for key,val in opts:
+            if key in ('-h', '--help'):
+                print_usage()
+                sys.exit(0)
+            elif key in ('-q', '--quiet'):
+                VERBOSITY =3D 0
+            elif key in ('-v', '--verbose'):
+                VERBOSITY =3D 2
+            elif key in ('-d', '--debug'):
+                PRINT_DEBUG =3D True
+            elif key in ('-p', '--pretend'):
+                PRETEND =3D True
+            elif key =3D=3D '--no-pretend':
+                NO_PRETEND =3D True
+            elif key in ('-e', '--exact'):
+                EXACT =3D True
+            elif key in ('-C', '--nocolor', '--no-color'):
+                nocolor()
+            elif key in ('-L', '--library', '--library=3D'):
+                _libs_to_check =3D _libs_to_check.union(val.split(','))
+            elif key in ('-i', '--ignore'):
+                USE_TMP_FILES =3D False
+
+        args =3D " ".join(args)
+    except getopt.GetoptError:
+        print_v(1, red('Unrecognized option\n'))
+        print_usage()
+        sys.exit(2)
+
+    if not sys.stdout.isatty():
+        nocolor()
+
+    if os.getuid() !=3D 0 and not PRETEND:
+        print_v(1, blue(' * ') + yellow('You are not root, adding --pret=
end to portage options'))
+        PRETEND =3D True
+    elif not PRETEND and IS_DEV and not NO_PRETEND:
+        print_v(1, blue(' * ') + yellow('This is dev. version, so it cou=
ld not work correctly'))
+        print_v(1, blue(' * ') + yellow('Adding --pretend to portage opt=
ions anyway'))
+        print_v(1, blue(' * ') + 'If you\'re sure, you can add --no-pret=
end to revdep options')
+        PRETEND =3D True
+
+
+    signal.signal(signal.SIGINT, exithandler)
+    signal.signal(signal.SIGTERM, exithandler)
+    signal.signal(signal.SIGPIPE, signal.SIG_DFL)
+
+    analyze_cache =3D {}
+    if USE_TMP_FILES and check_temp_files():
+        libraries, la_libraries, libraries_links, binaries =3D read_cach=
e()
+        assigned =3D analyse(libraries=3Dlibraries, la_libraries=3Dla_li=
braries, \
+                       libraries_links=3Dlibraries_links, binaries=3Dbin=
aries)
+    else:
+        assigned =3D analyse()
+
+    if not assigned:
+        print_v(1, '\n' + bold('Your system is consistent'))
+        sys.exit(0)
+
+    if EXACT:
+        emerge_command =3D '=3D' + ' =3D'.join(assigned)
+    else:
+        emerge_command =3D ' '.join(get_slotted_cps(assigned))
+    if PRETEND:
+        args +=3D ' --pretend'
+    if VERBOSITY >=3D 2:
+        args +=3D ' --verbose'
+    elif VERBOSITY < 1:
+        args +=3D ' --quiet'
+    emerge_command =3D args + ' --oneshot ' + emerge_command
+
+
+    #if PRETEND:
+    print_v(1, yellow('\nemerge') + bold(emerge_command))
+    #else:
+        #call_program(emerge_command.split())
+    os.system('emerge ' + emerge_command)
+