]> git.donarmstrong.com Git - neurodebian.git/blob - tools/reblender
44506cb7534515fd62646d80bf4260f8dfdb7c91
[neurodebian.git] / tools / reblender
1 #!/usr/bin/env python
2 """Tell me who you are!
3 """
4
5 import urllib
6 import apt
7 from debian_bundle import deb822
8 from debian_bundle import debtags
9 from ConfigParser import SafeConfigParser
10 import subprocess
11 import os
12 import sys
13 import shutil
14 import pysvn
15 from optparse import OptionParser, Option, OptionGroup, OptionConflictError
16
17
18 def transCodename(codename, cfg):
19     """Translate a known codename into a release description.
20
21     Unknown codenames will simply be returned as is.
22     """
23     # strip repository codename
24     codename = codename[codename.find('_') + 1:]
25
26     # if we know something, tell
27     if codename in cfg.options('release codenames'):
28         return cfg.get('release codenames', codename)
29     else:
30         return codename
31
32
33 def createDir(path):
34     if os.path.exists(path):
35         return
36
37     ps = path.split(os.path.sep)
38
39     for i in range(1,len(ps) + 1):
40         p = os.path.sep.join(ps[:i])
41
42         if not os.path.exists(p):
43             os.mkdir(p)
44
45
46 class AptListsCache(object):
47     def __init__(self, cachedir='cache', ro_cachedirs=None, init_db=None):
48         self.cachedir = cachedir
49
50         if not ro_cachedirs is None:
51             self.ro_cachedirs = ro_cachedirs
52         else:
53             self.ro_cachedirs = []
54
55         # always use system cache
56         #self.ro_cachedirs.append('/var/lib/apt/lists/')
57
58         # create cachedir
59         createDir(self.cachedir)
60
61
62     def get(self, url, update=False):
63         """Looks in the cache if the file is there and takes the cached one.
64         Otherwise it is downloaded first.
65
66         Knows how to deal with http:// and svn:// URLs.
67
68         :Return:
69           file handler
70         """
71         # look whether it is compressed
72         cext = url.split('.')[-1]
73         if cext in ['gz', 'bz2']:
74             target_url = url[:-1 * len(cext) -1]
75         else:
76             # assume not compressed
77             target_url = url
78             cext = None
79
80         # turn url into a filename -- mimik what APT does for
81         # /var/lib/apt/lists/
82         tfilename = '_'.join(target_url.split('/')[2:])
83
84         # if we need to download anyway do not search
85         if update:
86             cfilename = os.path.join(self.cachedir, tfilename)
87         else:
88             # look for the uncompressed file anywhere in the cache
89             cfilename = None
90             for cp in [self.cachedir] + self.ro_cachedirs:
91                 if os.path.exists(os.path.join(cp, tfilename)):
92                     cfilename = os.path.join(cp, tfilename)
93
94         # nothing found?
95         if cfilename is None:
96             # add cache item
97             cfilename = os.path.join(self.cachedir, tfilename)
98             update = True
99
100         # if updated needed -- download
101         if update:
102             print 'Caching file from %s' % url
103
104             if url.startswith('svn://'):
105                 # export from SVN
106                 pysvn.Client().export(url, cfilename)
107             if url.startswith('http://'):
108                 # download
109                 tempfile, ignored = urllib.urlretrieve(url)
110
111                 # decompress
112                 decompressor = None
113                 if cext == 'gz':
114                     decompressor = 'gzip'
115                 elif cext == 'bz2':
116                     decompressor = 'bzip2'
117                 elif cext == None:
118                     decompressor = None
119                 else:
120                     raise ValueError, \
121                           "Don't know how to decompress %s files" \
122                           % cext
123
124                 if not decompressor is None:
125                     if subprocess.call([decompressor, '-d', '-q', '-f',
126                                        tempfile]) == 1:
127                         raise RuntimeError, \
128                               "Something went wrong while decompressing '%s'" \
129                               % tempfile
130
131                 # move decompressed file into cache
132                 shutil.move(os.path.splitext(tempfile)[0], cfilename)
133
134                 # XXX do we need that if explicit filename is provided?
135                 urllib.urlcleanup()
136
137         # open cached file
138         fh = open(cfilename, 'r')
139
140         return fh
141
142
143
144
145 class DebianPkgArchive(SafeConfigParser):
146     """
147     """
148     def __init__(self, cache=None, init_db=None):
149         """
150         :Parameter:
151         """
152         SafeConfigParser.__init__(self)
153
154         # read an existing database if provided
155         if not init_db is None:
156             self.read(init_db)
157
158         # use provided file cache or use fresh one
159         if not cache is None:
160             self.cache = cache
161         else:
162             self.cache = AptListsCache()
163
164         # init debtags DB
165         self.dtags = debtags.DB()
166         self.dtags.read(open('/var/lib/debtags/package-tags'))
167
168         # init package filter
169         self.pkgfilter = None
170
171         self._updateReleases()
172
173
174     def _updateReleases(self):
175         self.releases = {}
176
177         # for all packages
178         for p in self.sections():
179             # no releases, nothing to do
180             if not self.has_option(p, 'releases'):
181                 continue
182
183             # for all releases of this package
184             for r in \
185               [rel.strip() for rel in self.get(p, 'releases').split(',')]:
186                   # push release code
187                   if not self.releases.has_key(r):
188                       self.releases[r] = []
189
190                   # store component
191                   component = self.get(p, '%s component' % r)
192
193                   if not component in self.releases[r]:
194                       self.releases[r].append(component)
195
196
197     def __repr__(self):
198         """Generate INI file content for current content.
199         """
200         # make adaptor to use str as file-like (needed for ConfigParser.write()
201         class file2str(object):
202             def __init__(self):
203                 self.__s = ''
204             def write(self, val):
205                 self.__s += val
206             def str(self):
207                 return self.__s
208
209         r = file2str()
210         self.write(r)
211
212         return r.str()
213
214
215     def save(self, filename):
216         """Write current content to a file.
217         """
218         f = open(filename, 'w')
219         self.write(f)
220         f.close()
221
222
223     def ensureUnique(self, section, option, value):
224         if not self.has_option(section, option):
225             self.set(section, option, value)
226         else:
227             if not self.get(section, option) == value:
228                 raise ValueError, "%s: %s is not unique (%s != %s)" \
229                                   % (section, option,
230                                      self.get(section, option), value)
231
232
233     def appendUniqueCSV(self, section, option, value):
234         """
235         """
236         if not self.has_option(section, option):
237             self.set(section, option, value)
238         else:
239             l = self.get(section, option).split(', ')
240             if not value in l:
241                 self.set(section, option, ', '.join(l + [value]))
242
243
244     def getReleaseInfo(self, rurl, force_update=False):
245         # root URL of the repository
246         baseurl = '/'.join(rurl.split('/')[:-1])
247         # get the release file from the cache
248         release_file = self.cache.get(rurl, update=force_update)
249
250         # create parser instance
251         rp = deb822.Release(release_file)
252
253         # architectures on this dist
254         archs = rp['Architectures'].split()
255         components = rp['Components'].split()
256         # compile a new codename that also considers the repository label
257         # to distinguish between official and unofficial repos.
258         label = rp['Label']
259         codename = rp['Codename']
260         labelcode = '_'.join([rp['Label'], rp['Codename']])
261
262         # cleanup
263         release_file.close()
264
265         return {'baseurl': baseurl, 'archs': archs, 'components': components,
266                 'codename': codename, 'label': label, 'labelcode': labelcode}
267
268
269     def checkOfficialRelease(self, rurl, force_update=False):
270         ri = self.getReleaseInfo(rurl, force_update=force_update)
271
272         # try with a i386 packages file, since that should be the most common
273         # one
274         # loop over all components
275         for c in ri['components']:
276             pkgsurl = self.buildPkgsURL(ri['baseurl'], c, 'i386')
277             packages_file = self.cache.get(pkgsurl,
278                                            update=force_update)
279
280             # now check every package, whether we also have it in the DB already
281             for st in deb822.Packages.iter_paragraphs(packages_file):
282                 pkg = st['Package']
283                 if self.has_section(pkg):
284                     # store the label code
285                     self.appendUniqueCSV(pkg, "releases", ri['labelcode'])
286                     # and the associated component
287                     self.ensureUnique(pkg, "%s component" % ri['labelcode'], c)
288                     # and version
289                     self.set(pkg, "%s version" % ri['labelcode'], st['Version'])
290
291             # cleanup
292             packages_file.close()
293
294
295     def buildPkgsURL(self, baseurl, component, arch):
296         return '/'.join([baseurl, component, 'binary-' + arch, 'Packages.bz2'])
297
298
299     def importRelease(self, rurl, force_update=False):
300
301         ri = self.getReleaseInfo(rurl, force_update=force_update)
302
303         # compile the list of Packages files to parse and parse them
304         for c in ri['components']:
305             for a in ri['archs']:
306                 # compile packages URL
307                 pkgsurl = self.buildPkgsURL(ri['baseurl'], c, a)
308
309                 # retrieve from cache
310                 packages_file = self.cache.get(pkgsurl,
311                                                update=force_update)
312
313                 # parse
314                 for stanza in deb822.Packages.iter_paragraphs(packages_file):
315                     self._storePkg(stanza, ri['labelcode'], c, ri['baseurl'])
316
317
318                 # cleanup
319                 packages_file.close()
320
321         self._updateReleases()
322
323
324     def _storePkg(self, st, codename, component, baseurl):
325         """
326         :Parameter:
327           st: Package section
328         """
329         pkg = st['Package']
330
331         if not self.has_section(pkg):
332             self.add_section(pkg)
333
334         # do nothing if package is not in filter if there is any
335         if not self.pkgfilter is None and not pkg in self.pkgfilter:
336             self.ensureUnique(pkg, 'visibility', 'shadowed')
337         else:
338             self.ensureUnique(pkg, 'visibility', 'featured')
339
340         # which releases
341         self.appendUniqueCSV(pkg, "releases", codename)
342
343         # arch listing
344         self.appendUniqueCSV(pkg, "%s archs" % codename, st['Architecture'])
345
346         # versions
347         self.ensureUnique(pkg,
348                           "%s version %s" % (codename, st['Architecture']),
349                           st['Version'])
350
351         # link to .deb
352         self.ensureUnique(pkg,
353                           "%s file %s" % (codename, st['Architecture']),
354                           '/'.join(baseurl.split('/')[:-2] + [st['Filename']]))
355
356         # component
357         self.ensureUnique(pkg, '%s component' % codename, component)
358
359         # store the pool url
360         self.ensureUnique(pkg, "%s poolurl" % codename,
361                  '/'.join(baseurl.split('/')[:-2] \
362                          + [os.path.dirname(st['Filename'])]))
363
364
365         # now the stuff where a single variant is sufficient and where we go for
366         # the latest available one
367         if self.has_option(pkg, "newest version") \
368             and apt.VersionCompare(st['Version'],
369                                    self.get(pkg, "newest version")) < 0:
370             return
371
372         # everything from here will overwrite existing ones
373
374         # we seems to have an updated package
375         self.set(pkg, "newest version", st['Version'])
376
377         # description
378         self.set(pkg, "description", st['Description'].replace('%', '%%'))
379
380         # maintainer
381         self.set(pkg, "maintainer", st['Maintainer'])
382
383         # optional stuff
384         if st.has_key('Homepage'):
385             self.set(pkg, 'homepage', st['Homepage'])
386
387         # query debtags
388         debtags = self.dtags.tagsOfPackage(pkg)
389         if debtags:
390             self.set(pkg, 'debtags', ', '.join(debtags))
391
392
393     def writeSourcesLists(self, outdir, cfg):
394         createDir(outdir)
395         createDir(os.path.join(outdir, 'static'))
396
397         fl = open(os.path.join(outdir, 'sources_lists'), 'w')
398         for trans, r in sorted([(transCodename(k, cfg), k) 
399                 for k in self.releases.keys()]):
400             # need to turn 'apsy_lenny' back into 'lenny'
401             debneuro_r = r.split('_')[1]
402
403             f = open(os.path.join(outdir, 'static',
404                                   'debneuro.%s.sources.list' % debneuro_r),
405                      'w')
406             f.write("deb http://apsy.gse.uni-magdeburg.de/debian %s %s\n" \
407                     % (debneuro_r, ' '.join(self.releases[r])))
408             f.write("deb-src http://apsy.gse.uni-magdeburg.de/debian %s %s\n" \
409                     % (debneuro_r, ' '.join(self.releases[r])))
410             # XXX use :download: role from sphinx 0.6 on
411             #fl.write('* `%s <http://apsy.gse.uni-magdeburg.de/debian/html/_static/debneuro.%s.sources.list>`_\n' \
412             fl.write('* `%s <_static/debneuro.%s.sources.list>`_\n' \
413                      % (trans, debneuro_r))
414             f.close()
415         fl.close()
416
417
418     def importProspectivePkgsFromTaskFile(self, url):
419         fh = self.cache.get(url)
420
421         for st in deb822.Packages.iter_paragraphs(fh):
422             # do not stop unless we have a description
423             if not st.has_key('Pkg-Description'):
424                 continue
425
426             if st.has_key('Depends'):
427                 pkg = st['Depends']
428             elif st.has_key('Suggests'):
429                 pkg = st['Suggests']
430             else:
431                 print 'Warning: Cannot determine name of prospective package ' \
432                       '... ignoring.'
433                 continue
434
435             # store pkg info
436             if not self.has_section(pkg):
437                 self.add_section(pkg)
438
439             # prospective ones are always featured
440             self.ensureUnique(pkg, 'visibility', 'featured')
441
442             # pkg description
443             self.set(pkg, "description",
444                      st['Pkg-Description'].replace('%', '%%'))
445
446             # optional stuff
447             if st.has_key('Homepage'):
448                 self.set(pkg, 'homepage', st['Homepage'])
449
450             if st.has_key('Pkg-URL'):
451                 self.set(pkg, 'external pkg url', st['Pkg-URL'])
452
453             if st.has_key('WNPP'):
454                 self.set(pkg, 'wnpp debian', st['WNPP'])
455
456             if st.has_key('License'):
457                 self.set(pkg, 'license', st['License'])
458
459             # treat responsible as maintainer
460             if st.has_key('Responsible'):
461                 self.set(pkg, "maintainer", st['Responsible'])
462
463
464     def setPkgFilterFromTaskFile(self, urls):
465         pkgs = []
466
467         for task in urls:
468             fh = self.cache.get(task)
469
470
471             # loop over all stanzas
472             for stanza in deb822.Packages.iter_paragraphs(fh):
473                 if stanza.has_key('Depends'):
474                     pkg = stanza['Depends']
475                 elif stanza.has_key('Suggests'):
476                     pkg = stanza['Suggests']
477                 else:
478                     continue
479
480                 # account for multiple packages per line
481                 if pkg.count(','):
482                     pkgs += [p.strip() for p in pkg.split(',')]
483                 else:
484                     pkgs.append(pkg.strip())
485
486         # activate filter
487         self.pkgfilter = pkgs
488
489
490 def genPkgPage(db, pkg, cfg):
491     """
492     :Parameters:
493       db: database
494       pkg: str
495         Package name
496     """
497     descr = db.get(pkg, 'description').split('\n')
498
499     s = ''
500
501     # only put index markup for featured packages
502     if db.get(pkg, 'visibility') == 'featured':
503         s = '.. index:: %s, ' % pkg
504         s += '\n'
505
506         # add a subset of available debtags (if present)
507         if db.has_option(pkg, 'debtags'):
508             # filter tags
509             tags = [t for t in db.get(pkg, 'debtags').split(', ')
510                         if t.split('::')[0] in ['field', 'works-with']]
511             if len(tags):
512                 s += '.. index:: %s\n\n' % ', '.join(tags)
513
514     # main ref target for this package
515     s += '.. _deb_' + pkg + ':\n'
516
517     # separate header from the rest
518     s += '\n\n\n'
519
520     header = '%s -- %s' % (pkg, descr[0])
521     s += '*' * (len(header) + 2)
522     s += '\n ' + header + '\n'
523     s += '*' * (len(header) + 2) + '\n\n'
524
525     # put description
526     # XXX honour formating syntax
527     s += '\n'.join([l.lstrip(' .') for l in descr[1:]])
528     s += '\n'
529
530     if db.has_option(pkg, 'homepage'):
531         s += '\n**Homepage**: %s\n' % db.get(pkg, 'homepage')
532
533     s += '\nBinary packages'\
534          '\n===============\n'
535
536     s += genMaintainerSection(db, pkg)
537
538     if db.has_option(pkg, 'wnpp debian'):
539         s += """\
540 A Debian packaging effort has been officially announced. Please see the
541 corresponding `intent-to-package bug report`_ for more information about
542 its current status.
543
544 .. _intent-to-package bug report: http://bugs.debian.org/%s
545
546 """ % db.get(pkg, 'wnpp debian')
547
548     # write repository content summary for NeuroDebian
549     s += getReposContentSummary(db, cfg, 'apsy', pkg)
550     # see if there is something about a package in Debian proper
551     s += getDebianRefs(db, cfg, pkg)
552
553     return s
554
555
556 def genMaintainerSection(db, pkg):
557     s = ''
558
559     if not db.has_option(pkg, 'maintainer'):
560         s += """\
561
562 Currently, nobody seems to be responsible for creating or maintaining
563 Debian packages of this software.
564
565 """
566         return s
567
568     # there is someone responsible
569     maintainer = db.get(pkg, 'maintainer')
570
571     # do we have actual packages, or is it just a note
572     if not db.has_option(pkg, 'releases'):
573         s += """\
574
575 There are currently no binary packages available. However, the last known
576 packaging effort was started by %s which meanwhile might have led to an
577 initial unofficial Debian packaging.
578
579 """ % maintainer
580         return s
581
582     s += '\n**Maintainer**: %s\n\n' % maintainer
583
584     if not maintainer.startswith('Michael Hanke'):
585         s += """\
586
587 .. note::
588   Do not contact the original package maintainer regarding
589   bugs in this unofficial binary package. Instead, contact
590   the repository maintainer at michael.hanke@gmail.com\ .
591
592 """
593
594     return s
595
596
597 def getDebianRefs(db, cfg, pkg):
598     # no release, nothing to do
599     if not db.has_option(pkg, 'releases'):
600         return ''
601     # which Debian release is this package part of?
602     debrels = [r.split('_')[1] for r in db.get(pkg, 'releases').split(', ')
603                     if r.startswith('Debian')]
604     # do nothing if there is no package in Debian proper
605     if not len(debrels):
606         return ''
607
608     s = """\
609 Official Debian archive
610 -----------------------
611
612 This package is available from the offical Debian archive for:
613
614 * %s
615
616 Please see the following ressources for more information:
617
618 * `Debian package summary page`_
619 * `Bugreports in the Debian bug tracking system`_
620 * `Debian package popularity statistics`_
621
622 .. _Debian package summary page: http://packages.debian.org/%s
623 .. _Bugreports in the Debian bug tracking system: http://bugs.debian.org/%s
624 .. _Debian package popularity statistics: http://qa.debian.org/popcon.php?package=%s
625
626 """ % ('\n* '.join(['`%s <http://www.debian.org/releases/%s>`_ *[%s]*: %s' \
627                     % (transCodename(rel, cfg),
628                        rel,
629                        db.get(pkg, 'debian_%s component' % rel),
630                        db.get(pkg, 'debian_%s version' % rel))
631                             for rel in debrels]),
632        pkg, pkg, pkg)
633
634     return s
635
636
637 def getReposContentSummary(db, cfg, reposlabel, pkg):
638     # do nothing if the are no packages
639     if not db.has_option(pkg, 'releases'):
640         return ''
641
642     reposname = cfg.get('repository labels', reposlabel)
643     s = '\n%s\n%s\n' % (reposname, '-' * len(reposname))
644
645     s += """\
646 The repository contains binary packages for the following distribution
647 releases and system architectures. The corresponding source packages
648 are available too.
649
650 .. note::
651   Do not download this package manually if you plan to use it
652   regularly. Instead configure your package manager to use this
653   repository by following the instructions on the
654   :ref:`front page <repository_howto>`.
655
656 """
657
658     # for all releases this package is part of
659     for rel in db.get(pkg, 'releases').split(', '):
660         # ignore items associated with other repositories
661         if not rel.split('_')[0] == reposlabel:
662             continue
663         # write release description and component
664         s += '\n%s *[%s]*:\n  ' \
665                 % (transCodename(rel, cfg),
666                    db.get(pkg, '%s component' % rel))
667
668         s += '`source <%s>`_' % db.get(pkg, '%s poolurl' % rel)
669
670         # archs this package is available for
671         archs = db.get(pkg, '%s archs' % rel).split(', ')
672
673         # extract all present versions for any arch
674         versions =  [db.get(pkg, '%s version %s' % (rel, arch))
675                         for arch in archs]
676
677         # if there is only a single version for all of them, simplify the list
678         single_ver = versions.count(versions[0]) == len(versions)
679
680         if single_ver:
681             s += ', '
682             # only one version string for all
683             s += ', '.join(['`%s <%s>`_' \
684                     % (arch, db.get(pkg, '%s file %s' % (rel, arch)))
685                         for arch in archs])
686             s += ' (%s)' % versions[0]
687         else:
688             s += ', '
689             # a separate version string for each arch
690             s += ', '.join(['`%s <%s>`_ (%s)' \
691                     % (arch,
692                        db.get(pkg, '%s file %s' % (rel, arch)),
693                        db.get(pkg, '%s version %s' % (rel, arch)))
694                         for arch in archs])
695
696         s += '\n'
697
698     return s
699
700 def maintainer2email(maint):
701     return maint.split('<')[1].rstrip('>')
702
703
704 def writePkgsBy(db, key, value2id, outdir, heading):
705     createDir(outdir)
706     nwkey = key.replace(' ', '')
707     createDir(os.path.join(outdir, 'by%s' % nwkey))
708
709     collector = {}
710
711     # get packages by maintainer
712     for p in db.sections():
713         # only featured packages
714         if db.get(p, 'visibility') == 'shadowed':
715             continue
716
717         if db.has_option(p, key):
718             by = db.get(p, key)
719
720             if not collector.has_key(by):
721                 collector[by] = (value2id(by), [p])
722             else:
723                 collector[by][1].append(p)
724
725     toc = open(os.path.join(outdir, 'by%s.rst' % nwkey), 'w')
726     toc.write('.. index:: Packages by %s\n.. _by%s:\n\n' % (key, key))
727
728     toc_heading = 'Packages by %s' % key
729     toc.write('%s\n%s\n\n' % (toc_heading, '=' * len(toc_heading)))
730     toc.write('.. toctree::\n  :maxdepth: 1\n\n')
731
732     # summary page per maintainer
733     for by in sorted(collector.keys()):
734         toc.write('  by%s/%s\n' % (nwkey, collector[by][0]))
735
736         fh = open(os.path.join(outdir,
737                                'by%s' % nwkey,
738                                collector[by][0] + '.rst'), 'w')
739
740         fh.write('.. index:: %s\n.. _%s:\n\n' % (by, by))
741
742         hdr = heading.replace('<ITEM>', by)
743         fh.write(hdr + '\n')
744         fh.write('=' * len(hdr) + '\n\n')
745
746         # write sorted list of packages
747         for p in sorted(collector[by][1]):
748             fh.write('* :ref:`deb_%s`\n' % p)
749
750         fh.close()
751
752     toc.close()
753
754
755 def writeRst(db, outdir, cfg, addenum_dir=None):
756     createDir(outdir)
757     createDir(os.path.join(outdir, 'pkgs'))
758
759     # open pkgs toctree
760     toc = open(os.path.join(outdir, 'pkgs.rst'), 'w')
761     # write header
762     toc.write('.. _full_pkg_list:\n\n')
763     toc.write('Archive content\n===============\n\n'
764               '.. toctree::\n  :maxdepth: 1\n\n')
765
766     for p in sorted(db.sections()):
767         print "Generating page for '%s'" % p
768         pf = open(os.path.join(outdir, 'pkgs', '%s.rst' % p), 'w')
769         pf.write(genPkgPage(db, p, cfg))
770
771         # check for doc addons
772         if addenum_dir is not None:
773             addenum = os.path.join(os.path.abspath(addenum_dir), '%s.rst' % p)
774             if os.path.exists(addenum):
775                 pf.write('\n\n.. include:: %s\n' % addenum)
776         pf.close()
777         toc.write('  pkgs/%s\n' % p)
778
779
780     toc.close()
781
782
783 def prepOptParser(op):
784     # use module docstring for help output
785     op.usage = "%s [OPTIONS]\n\n" % sys.argv[0] + __doc__
786
787     op.add_option("--db",
788                   action="store", type="string", dest="db",
789                   default=None,
790                   help="Database file to read. Default: None")
791
792     op.add_option("--cfg",
793                   action="store", type="string", dest="cfg",
794                   default=None,
795                   help="Repository config file.")
796
797     op.add_option("-o", "--outdir",
798                   action="store", type="string", dest="outdir",
799                   default=None,
800                   help="Target directory for ReST output. Default: None")
801
802     op.add_option("-r", "--release-url",
803                   action="append", dest="release_urls",
804                   help="None")
805
806     op.add_option("--pkgaddenum", action="store", dest="addenum_dir",
807                   type="string", default=None, help="None")
808
809
810
811 def main():
812     op = OptionParser(version="%prog 0.0.1")
813     prepOptParser(op)
814
815     (opts, args) = op.parse_args()
816
817     if len(args) != 1:
818         print('There needs to be exactly one command')
819         sys.exit(1)
820
821     cmd = args[0]
822
823     if opts.cfg is None:
824         print("'--cfg' option is mandatory.")
825         sys.exit(1)
826
827
828     cfg = SafeConfigParser()
829     cfg.read(opts.cfg)
830
831     # load existing db, unless renew is requested
832     if cmd == 'refreshdb':
833         dpa = DebianPkgArchive()
834     else:
835         dpa = DebianPkgArchive(init_db=opts.db)
836
837
838     if cmd == 'generate':
839         if opts.outdir is None:
840             print('Not output directory specified!')
841             sys.exit(1)
842
843         dpa.writeSourcesLists(opts.outdir, cfg)
844         writeRst(dpa, opts.outdir, cfg, opts.addenum_dir)
845         writePkgsBy(dpa, 'maintainer', maintainer2email, opts.outdir,
846                     'Packages maintained by <ITEM>')
847         # stop here
848         sys.exit(0)
849
850
851     if cfg.has_option('packages', 'select taskfiles'):
852         dpa.setPkgFilterFromTaskFile(cfg.get('packages',
853                                              'select taskfiles').split())
854
855     if cfg.has_option('packages', 'select names'):
856         dpa.pkgfilter += cfg.get('packages', 'select names').split()
857
858     if cfg.has_option('packages', 'prospective'):
859         for p in cfg.get('packages', 'prospective').split():
860             dpa.importProspectivePkgsFromTaskFile(p)
861
862     if cfg.has_option('repositories', 'releases'):
863         for rurl in cfg.get('repositories', 'releases').split():
864             dpa.importRelease(rurl, force_update=False)
865
866     if cfg.has_option('repositories', 'releases'):
867         for rurl in cfg.get('repositories', 'releases').split():
868             dpa.importRelease(rurl, force_update=False)
869
870     if cfg.has_option('officials', 'releases'):
871         for rurl in cfg.get('officials', 'releases').split():
872             dpa.checkOfficialRelease(rurl, force_update=False)
873
874     if not opts.db is None:
875         dpa.save(opts.db)
876
877
878 if __name__ == "__main__":
879     main()
880