resetting manifest requested domain to floor
[platform/upstream/createrepo.git] / genpkgmetadata.py
1 #!/usr/bin/python -t
2 # primary functions and glue for generating the repository metadata
3 #
4
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 2 of the License, or
8 # (at your option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU Library General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 # Copyright 2004 Duke University
19 # Portions Copyright 2009  Red Hat, Inc -
20 # written by seth vidal skvidal at fedoraproject.org
21
22 import os
23 import sys
24 import re
25 from optparse import OptionParser
26 import time
27
28 import createrepo
29 from createrepo import MDError
30 from createrepo.utils import errorprint, _
31 import yum.misc
32
33
34 def parse_args(args, conf):
35     """
36        Parse the command line args. return a config object.
37        Sanity check all the things being passed in.
38     """
39
40     _def   = yum.misc._default_checksums[0]
41     _avail = yum.misc._available_checksums
42     parser = OptionParser(version = "createrepo %s" % createrepo.__version__)
43     # query options
44     parser.add_option("-q", "--quiet", default=False, action="store_true",
45         help="output nothing except for serious errors")
46     parser.add_option("-v", "--verbose", default=False, action="store_true",
47         help="output more debugging info.")
48     parser.add_option("--profile", default=False, action="store_true",
49         help="output timing/profile info.")
50     parser.add_option("-x", "--excludes", default=[], action="append",
51         help="files to exclude")
52     parser.add_option("--basedir", default=os.getcwd(),
53         help="basedir for path to directories")
54     parser.add_option("-u", "--baseurl", default=None,
55         help="baseurl to append on all files")
56     parser.add_option("-g", "--groupfile", default=None,
57         help="path to groupfile to include in metadata")
58     parser.add_option("-s", "--checksum", default=_def, dest='sumtype',
59         help="specify the checksum type to use (default: %s)" % _def)
60     parser.add_option("-p", "--pretty", default=False, action="store_true",
61         help="make sure all xml generated is formatted")
62     parser.add_option("-c", "--cachedir", default=None,
63         help="set path to cache dir")
64     parser.add_option("-C", "--checkts", default=False, action="store_true",
65         help="check timestamps on files vs the metadata to see " \
66            "if we need to update")
67     parser.add_option("-d", "--database", default=True, action="store_true",
68         help="create sqlite database files: now default, see --no-database to disable")
69     parser.add_option("--no-database", default=False, dest="nodatabase", action="store_true",
70         help="do not create sqlite dbs of metadata")
71     # temporarily disabled
72     #parser.add_option("--database-only", default=False, action="store_true",
73     #  dest='database_only',
74     #  help="Only make the sqlite databases - does not work with --update, yet")
75     parser.add_option("--update", default=False, action="store_true",
76         help="use the existing repodata to speed up creation of new")
77     parser.add_option("--update-md-path", default=None, dest='update_md_path',
78         help="use the existing repodata  for --update from this path")
79     parser.add_option("--skip-stat", dest='skip_stat', default=False,
80         help="skip the stat() call on a --update, assumes if the file" \
81              "name is the same then the file is still the same " \
82              "(only use this if you're fairly trusting or gullible)",
83         action="store_true")
84     parser.add_option("--split", default=False, action="store_true",
85         help="generate split media")
86     parser.add_option("-i", "--pkglist", default=None,
87         help="use only the files listed in this file from the " \
88              "directory specified")
89     parser.add_option("-n", "--includepkg", default=[], action="append",
90         help="add this pkg to the list - can be specified multiple times")
91     parser.add_option("-o", "--outputdir", default=None,
92         help="<dir> = optional directory to output to")
93     parser.add_option("-S", "--skip-symlinks", dest="skip_symlinks",
94         default=False, action="store_true", help="ignore symlinks of packages")
95     parser.add_option("--changelog-limit", dest="changelog_limit",
96         default=None, help="only import the last N changelog entries")
97     parser.add_option("--unique-md-filenames", dest="unique_md_filenames",
98         help="include the file's checksum in the filename, helps with proxies",
99         default=True, action="store_true")
100     parser.add_option("--simple-md-filenames", dest="simple_md_filenames",
101         help="do not include the file's checksum in the filename, helps with proxies",
102         default=False, action="store_true")
103     parser.add_option("--distro", default=[], action="append",
104         help="distro tag and optional cpeid: --distro" "'cpeid,textname'")
105     parser.add_option("--content", default=[], dest='content_tags',
106         action="append", help="tags for the content in the repository")
107     parser.add_option("--repo", default=[], dest='repo_tags', 
108         action="append", help="tags to describe the repository itself")
109     parser.add_option("--revision", default=None,
110         help="user-specified revision for this repository")
111     parser.add_option("--deltas", default=False, action="store_true",
112         help="create delta rpms and metadata")
113     parser.add_option("--oldpackagedirs", default=[], dest="oldpackage_paths",
114         action="append", help="paths to look for older pkgs to delta against")
115     parser.add_option("--num-deltas", default=1, dest='num_deltas', type='int',
116         help="the number of older versions to make deltas against")
117     parser.add_option("--read-pkgs-list", default=None, dest='read_pkgs_list',
118         help="output the paths to the pkgs actually read useful with --update")
119     parser.add_option("--max-delta-rpm-size", default=100000000,
120         dest='max_delta_rpm_size', type='int',
121         help="max size of an rpm that to run deltarpm against (in bytes)")
122
123     parser.add_option("--workers", default=1,
124         dest='workers', type='int',
125         help="number of workers to spawn to read rpms")
126     
127     (opts, argsleft) = parser.parse_args(args)
128     if len(argsleft) > 1 and not opts.split:
129         errorprint(_('Error: Only one directory allowed per run.'))
130         parser.print_usage()
131         sys.exit(1)
132
133     elif len(argsleft) == 0:
134         errorprint(_('Error: Must specify a directory to index.'))
135         parser.print_usage()
136         sys.exit(1)
137
138     else:
139         directories = argsleft
140
141     if opts.sumtype == 'sha1':
142         errorprint(_('Warning: It is more compatible to use sha instead of sha1'))
143
144     if opts.sumtype != 'sha' and opts.sumtype not in _avail:
145         errorprint(_('Error: Checksum %s not available (sha, %s)') %
146                    (opts.sumtype, ", ".join(sorted(_avail))))
147         sys.exit(1)
148
149     if opts.split and opts.checkts:
150         errorprint(_('--split and --checkts options are mutually exclusive'))
151         sys.exit(1)
152
153     if opts.simple_md_filenames:
154         opts.unique_md_filenames = False
155     
156     if opts.nodatabase:
157         opts.database = False
158         
159     # let's switch over to using the conf object - put all the opts into it
160     for opt in parser.option_list:
161         if opt.dest is None: # this is fairly silly
162             continue
163         # if it's not set, take the default from the base class
164         if getattr(opts, opt.dest) is None:
165             continue
166         setattr(conf, opt.dest, getattr(opts, opt.dest))
167
168     directory = directories[0]
169     conf.directory = directory
170     conf.directories = directories
171
172     # distro tag parsing
173
174     for spec in opts.distro:
175         if spec.find(',') == -1:
176             conf.distro_tags.append((None, spec))
177         else:
178             splitspec = spec.split(',')
179             conf.distro_tags.append((splitspec[0], splitspec[1]))
180
181     lst = []
182     if conf.pkglist:
183         pfo = open(conf.pkglist, 'r')
184         for line in pfo.readlines():
185             line = line.strip()
186             if re.match('^\s*\#.*', line) or re.match('^\s*$', line):
187                 continue
188             lst.append(line)
189         pfo.close()
190
191     conf.pkglist = lst
192
193     if conf.includepkg:
194         conf.pkglist.extend(conf.includepkg)
195
196     if conf.changelog_limit: # make sure it is an int, not a string
197         conf.changelog_limit = int(conf.changelog_limit)
198
199     return conf
200
201 class MDCallBack(object):
202     """cli callback object for createrepo"""
203     def __init__(self):
204         self.__show_progress = os.isatty(1)
205
206     def errorlog(self, thing):
207         """error log output"""
208         print >> sys.stderr, thing
209
210     def log(self, thing):
211         """log output"""
212         print thing
213
214     def progress(self, item, current, total):
215         """progress bar"""
216         
217         if not self.__show_progress:
218             return
219         beg = "%*d/%d - " % (len(str(total)), current, total)
220         left = 80 - len(beg)
221         sys.stdout.write("\r%s%-*.*s" % (beg, left, left, item))
222         sys.stdout.flush()
223
224 def main(args):
225     """createrepo from cli main flow"""
226     start_st = time.time()
227     conf = createrepo.MetaDataConfig()
228     conf = parse_args(args, conf)
229     if conf.profile:
230         print ('start time: %0.3f' % (time.time() - start_st))
231
232     mid_st = time.time()
233     try:
234         if conf.split:
235             mdgen = createrepo.SplitMetaDataGenerator(config_obj=conf,
236                                                       callback=MDCallBack())
237         else:
238             mdgen = createrepo.MetaDataGenerator(config_obj=conf,
239                                                  callback=MDCallBack())
240             if mdgen.checkTimeStamps():
241                 if mdgen.conf.verbose:
242                     print _('repo is up to date')
243                 sys.exit(0)
244
245         if conf.profile:
246             print ('mid time: %0.3f' % (time.time() - mid_st))
247
248         pm_st = time.time()
249         mdgen.doPkgMetadata()
250         if conf.profile:
251             print ('pm time: %0.3f' % (time.time() - pm_st))
252         rm_st = time.time()
253         mdgen.doRepoMetadata()
254         if conf.profile:
255             print ('rm time: %0.3f' % (time.time() - rm_st))
256         fm_st = time.time()
257         mdgen.doFinalMove()
258         if conf.profile:
259             print ('fm time: %0.3f' % (time.time() - fm_st))
260
261
262     except MDError, errormsg:
263         errorprint(_('%s') % errormsg)
264         sys.exit(1)
265
266
267 if __name__ == "__main__":
268     if len(sys.argv) > 1:
269         if sys.argv[1] == 'profile':
270             import hotshot
271             p = hotshot.Profile(os.path.expanduser("~/createrepo.prof"))
272             p.run('main(sys.argv[2:])')
273             p.close()
274         else:
275             main(sys.argv[1:])
276     else:
277         main(sys.argv[1:])