~ubuntu-branches/ubuntu/trusty/germinate/trusty-proposed

« back to all changes in this revision

Viewing changes to bin/germinate-pkg-diff

  • Committer: Package Import Robot
  • Author(s): Colin Watson
  • Date: 2011-12-04 14:16:54 UTC
  • Revision ID: package-import@ubuntu.com-20111204141654-pumqjv3x5x0a4lgi
Tags: 2.0
* Make sure to always close files after finishing with them.  Mostly this
  is done using the 'with' statement in Python 2.6, but pychecker gets
  unhappy with contextlib.closing so I carried on using traditional
  try/finally blocks in cases that would require that.
* Remove all uses of os.system and os.popen, replacing them with uses of
  the better-designed subprocess module.
* Remove all code supporting the germinate -i/--ipv6 option; this has been
  off by default since November 2004, the service behind this was
  discontinued in March 2007
  (http://lists.debian.org/debian-ipv6/2007/02/msg00015.html), and
  germinate was never a great place to track this kind of thing anyway.
* Convert all option parsing to optparse.  Consolidate defaults into a new
  Germinate.defaults module.
* Update copyright dates.
* Move canonical source location from people.canonical.com to a hosted
  branch on Launchpad.
* Slightly modernise use of dh_python2.
* Forbid seed names containing slashes.
* Eliminate most uses of list.sort() in favour of sorted(iterable).
* When promoting dependencies from lesser seeds, remove them from the
  lesser seed lists at output time rather than immediately.  This is
  mostly to make it easier to process multiple seed structures, but also
  fixes a long-standing bug where promoted dependencies were only removed
  from a single arbitrary lesser seed rather than from all possible ones.
* Memoise the results of Germinator's _inner_seeds, _strictly_outer_seeds,
  and _outer_seeds methods.  This saves nearly a third of germinate's
  runtime in common cases.
* Write all output files atomically.
* Change default distribution to precise.
* Update kubuntu-meta example in germinate-update-metapackage(1).
* Refer to versioned GPL file in debian/copyright.
* Policy version 3.9.2: no changes required.

* Massive API cleanup:
  - Move output-writing functions from the top-level germinate program
    into Germinator.
  - Redesign how Germinator gets Packages/Sources sections from the
    archive.  This now works via an abstract interface, which should make
    it easier to plug in alternative archive sources (e.g. a database).
  - Move all apt_pkg interaction into library code.  Germinator.__init__
    now takes an architecture argument so that it can set
    APT::Architecture.
  - Turn open_seed into a Seed class, allowing it to be a context manager.
  - Move code pertaining to the structure of seeds into a SeedStructure
    class, simplifying the interface.
  - Make all module names lower-case, per PEP-8.  Remove the separate
    Germinate.Archive.tagfile module; this is now in germinate.archive
    directly.  Adjust build system and pychecker handling to support this.
  - Remove unnecessary logging helper functions.
  - Don't modify level names on the root logger simply as a result of
    importing germinate.germinator; move this into a function.
  - Prefix all private methods with an underscore.
  - Remove germinate.tsort from germinate's public API.
  - Convert all method names to the PEP-8 preferred style (method_name
    rather than methodName).
  - Introduce wrapper functions for the various uses of write_list and
    write_source_list, and make the underlying methods private.
  - Make most instance variables private by prefixing an underscore,
    adding a few accessor methods.
  - Convert build system to distutils, make the germinate Python package
    public, and create a new python-germinate binary package.
  - Improve the Seed class so that seeds can be read multiple times
    without having to redownload them, and so that they remember which
    branch they came from.
  - Don't modify inner seeds when processing outer ones; filter
    build-dependencies on output instead.
  - Don't plant or grow seeds that have already had functionally-identical
    versions planted or grown respectively.
  - Automatically convert string dists/components/mirrors/source_mirrors
    arguments to lists in TagFile constructor.
  - Make it possible for a single Germinator to process multiple seed
    structures, reusing the work done on common seeds.
  - Canonicalise mirrors (by appending '/' if necessary) in TagFile rather
    than in the main germinate program.
  - Handle the extra seed entirely within Germinator rather than modifying
    SeedStructure (which doesn't fit well with processing the same seed
    structure on multiple architectures).
  - Use module-level loggers.
  - Get rid of the custom PROGRESS log level.
  - Change germinate.archive to use logging rather than print.
  - Add docstrings for all public classes and methods, and tidy up a few
    existing ones per PEP-257.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/env python
 
2
# -*- coding: UTF-8 -*-
 
3
 
 
4
# Copyright (c) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011
 
5
#               Canonical Ltd.
 
6
#
 
7
# This file is part of Germinate.
 
8
#
 
9
# Germinate is free software; you can redistribute it and/or modify it
 
10
# under the terms of the GNU General Public License as published by the
 
11
# Free Software Foundation; either version 2, or (at your option) any
 
12
# later version.
 
13
#
 
14
# Germinate is distributed in the hope that it will be useful, but
 
15
# WITHOUT ANY WARRANTY; without even the implied warranty of
 
16
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
17
# General Public License for more details.
 
18
#
 
19
# You should have received a copy of the GNU General Public License
 
20
# along with Germinate; see the file COPYING.  If not, write to the Free
 
21
# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 
22
# 02110-1301, USA.
 
23
 
 
24
import sys
 
25
import os
 
26
import optparse
 
27
import logging
 
28
import subprocess
 
29
 
 
30
try:
 
31
    import imp
 
32
    imp.find_module('germinate.germinator')
 
33
except ImportError:
 
34
    # Running from build tree?
 
35
    sys.path.insert(0, os.path.join(sys.path[0], os.pardir))
 
36
 
 
37
from germinate.germinator import Germinator
 
38
import germinate.archive
 
39
import germinate.defaults
 
40
from germinate.log import germinate_logging
 
41
from germinate.seeds import SeedError, SeedStructure
 
42
import germinate.version
 
43
 
 
44
MIRRORS = [germinate.defaults.mirror]
 
45
COMPONENTS = ["main"]
 
46
 
 
47
class Package:
 
48
    def __init__(self, name):
 
49
        self.name = name
 
50
        self.seed = {}
 
51
        self.installed = 0
 
52
 
 
53
    def set_seed(self, seed):
 
54
        self.seed[seed] = 1
 
55
 
 
56
    def set_installed(self):
 
57
        self.installed = 1
 
58
 
 
59
    def output(self, outmode):
 
60
        ret = self.name.ljust(30) + "\t"
 
61
        if outmode == "i":
 
62
            if self.installed and not len(self.seed):
 
63
                ret += "deinstall"
 
64
            elif not self.installed and len(self.seed):
 
65
                ret += "install"
 
66
            else:
 
67
                return ""
 
68
        elif outmode == "r":
 
69
            if self.installed and not len(self.seed):
 
70
                ret += "install"
 
71
            elif not self.installed and len(self.seed):
 
72
                ret += "deinstall"
 
73
            else:
 
74
                return ""
 
75
        else:           # default case
 
76
            if self.installed and not len(self.seed):
 
77
                ret = "- " + ret
 
78
            elif not self.installed and len(self.seed):
 
79
                ret = "+ " + ret
 
80
            else:
 
81
                ret = "  " + ret
 
82
            ret += ",".join(sorted(self.seed.keys()))
 
83
        return ret
 
84
 
 
85
 
 
86
class Globals:
 
87
    def __init__(self):
 
88
        self.package = {}
 
89
        self.seeds = []
 
90
        self.outputs = {}
 
91
        self.outmode = ""
 
92
 
 
93
    def set_seeds(self, options, seeds):
 
94
        self.seeds = seeds
 
95
 
 
96
        # Suppress most log information
 
97
        germinate_logging(logging.CRITICAL)
 
98
        logging.getLogger('germinate.archive').setLevel(logging.INFO)
 
99
 
 
100
        global MIRRORS, COMPONENTS
 
101
        print "Germinating"
 
102
        g = Germinator(options.arch)
 
103
 
 
104
        archive = germinate.archive.TagFile(
 
105
            options.dist, COMPONENTS, options.arch, MIRRORS, cleanup=True)
 
106
        g.parse_archive(archive)
 
107
 
 
108
        needed_seeds = []
 
109
        build_tree = False
 
110
        try:
 
111
            structure = SeedStructure(options.release, options.seeds)
 
112
            for seedname in self.seeds:
 
113
                if seedname == ('%s+build-depends' % structure.supported):
 
114
                    seedname = structure.supported
 
115
                    build_tree = True
 
116
                needed_seeds.append(seedname)
 
117
            g.plant_seeds(structure, seeds=needed_seeds)
 
118
        except SeedError:
 
119
            sys.exit(1)
 
120
        g.grow(structure)
 
121
 
 
122
        for seedname in structure.names:
 
123
            for pkg in g.get_seed_entries(structure, seedname):
 
124
                self.package.setdefault(pkg, Package(pkg))
 
125
                self.package[pkg].set_seed(seedname + ".seed")
 
126
            for pkg in g.get_seed_recommends_entries(structure, seedname):
 
127
                self.package.setdefault(pkg, Package(pkg))
 
128
                self.package[pkg].set_seed(seedname + ".seed-recommends")
 
129
            for pkg in g.get_depends(structure, seedname):
 
130
                self.package.setdefault(pkg, Package(pkg))
 
131
                self.package[pkg].set_seed(seedname + ".depends")
 
132
 
 
133
            if build_tree:
 
134
                build_depends = set(g.get_build_depends(structure, seedname))
 
135
                for inner in structure.inner_seeds(structure.supported):
 
136
                    build_depends -= set(g.get_seed_entries(structure, inner))
 
137
                    build_depends -= set(g.get_seed_recommends_entries(
 
138
                        structure, inner))
 
139
                    build_depends -= g.get_depends(structure, inner)
 
140
                for pkg in build_depends:
 
141
                    self.package.setdefault(pkg, Package(pkg))
 
142
                    self.package[pkg].set_seed(structure.supported +
 
143
                                               ".build-depends")
 
144
 
 
145
    def parse_dpkg(self, fname):
 
146
        if fname is None:
 
147
            dpkg_cmd = subprocess.Popen(['dpkg', '--get-selections'],
 
148
                                        stdout=subprocess.PIPE)
 
149
            try:
 
150
                lines = dpkg_cmd.stdout.readlines()
 
151
            finally:
 
152
                dpkg_cmd.wait()
 
153
        else:
 
154
            with open(fname) as f:
 
155
                lines = f.readlines()
 
156
        for l in lines:
 
157
            pkg, st = l.split(None)
 
158
            self.package.setdefault(pkg, Package(pkg))
 
159
            if st == "install" or st == "hold":
 
160
                self.package[pkg].set_installed()
 
161
 
 
162
    def set_output(self, mode):
 
163
        self.outmode = mode
 
164
 
 
165
    def output(self):
 
166
        for k in sorted(self.package.keys()):
 
167
            l = self.package[k].output(self.outmode)
 
168
            if len(l):
 
169
                print l
 
170
 
 
171
 
 
172
def parse_options():
 
173
    epilog = '''\
 
174
A list of seeds against which to compare may be supplied as non-option
 
175
arguments.  Seeds from which they inherit will be added automatically.  The
 
176
default is 'desktop'.'''
 
177
 
 
178
    parser = optparse.OptionParser(
 
179
        prog='germinate-pkg-diff',
 
180
        usage='%prog [options] [seeds]',
 
181
        version='%prog ' + germinate.version.VERSION,
 
182
        epilog=epilog)
 
183
    parser.add_option('-l', '--list', dest='dpkg_file', metavar='FILE',
 
184
                      help='read list of packages from this file '
 
185
                           '(default: read from dpkg --get-selections)')
 
186
    parser.add_option('-m', '--mode', dest='mode', type='choice',
 
187
                      choices=('i', 'r', 'd'), default='d', metavar='[i|r|d]',
 
188
                      help='show packages to install/remove/diff (default: d)')
 
189
    parser.add_option('-S', '--seed-source', dest='seeds', metavar='SOURCE',
 
190
                      default=germinate.defaults.seeds,
 
191
                      help='fetch seeds from SOURCE (default: %s)' %
 
192
                           germinate.defaults.seeds)
 
193
    parser.add_option('-s', '--seed-dist', dest='release', metavar='DIST',
 
194
                      default=germinate.defaults.release,
 
195
                      help='fetch seeds for distribution DIST '
 
196
                           '(default: %default)')
 
197
    parser.add_option('-d', '--dist', dest='dist',
 
198
                      default=germinate.defaults.dist,
 
199
                      help='operate on distribution DIST (default: %default)')
 
200
    parser.add_option('-a', '--arch', dest='arch',
 
201
                      default=germinate.defaults.arch,
 
202
                      help='operate on architecture ARCH (default: %default)')
 
203
 
 
204
    options, args = parser.parse_args()
 
205
 
 
206
    options.seeds = options.seeds.split(',')
 
207
    options.dist = options.dist.split(',')
 
208
 
 
209
    return options, args
 
210
 
 
211
 
 
212
def main():
 
213
    g = Globals()
 
214
 
 
215
    options, args = parse_options()
 
216
 
 
217
    g.set_output(options.mode)
 
218
    g.parse_dpkg(options.dpkg_file)
 
219
    if not len(args):
 
220
        args = ["desktop"]
 
221
    g.set_seeds(options, args)
 
222
    g.output()
 
223
 
 
224
if __name__ == "__main__":
 
225
    main()