+def expand_pkgs(pkgs, topdir='.'):
+ """In-place modification of pkgs taking if necessary additional
+ information from Debian materials, and pruning empty fields
+ """
+ verbose(4, "Expanding content for %d packages" % len(pkgs))
+ debianm = None
+
+ # Expand packages which format is extended
+ for pkg in pkgs:
+ if pkg.format == 'extended':
+ # expanding, for that we need debian/control
+ if debianm is None:
+ debianm = DebianMaterials(topdir)
+ for k, m in (('License', lambda: debianm.get_license(pkg['Pkg-Name'])),
+ ('WNPP', debianm.get_wnpp),
+ ('Pkg-Description',
+ lambda: debianm.get_description(pkg['Pkg-Name'])),
+ ('Responsible', debianm.get_responsible),
+ ('Homepage', lambda: debianm.source.get('Homepage', None)),
+ ('Pkg-source', lambda: debianm.source.get('Source', None)),
+ ):
+ if pkg.get(k, None):
+ continue
+ v = m()
+ if v:
+ pkg[k] = v
+ # VCS fields
+ pkg.update(debianm.get_vcsfields())
+
+
+def prefix_index(x, entries, strict=True, case=False, default=10000):
+ """Returns an index for the x in entries
+ """
+ if not case:
+ x = x.lower()
+ for i, v in enumerate(entries):
+ if x.startswith(v):
+ return i
+
+ if strict:
+ raise IndexError(
+ "Could not find location for %s as specified by %s" %
+ (x, entries))
+ return default
+
+
+def key_prefix_compare(x, y, order, strict=True, case=False):
+ """Little helper to help with sorting
+
+ Sorts according to the order of string prefixes as given by
+ `order`. If `strict`, then if no matching prefix found, would
+ raise KeyError; otherwise provides least priority to those keys
+ which were not found in `order`
+ """
+ if not case:
+ order = [v.lower() for v in order]
+
+ cmp_res = cmp(prefix_index(x[0], order, strict, case),
+ prefix_index(y[0], order, strict, case))
+ if not cmp_res: # still unknown
+ return cmp(x, y)
+ return cmp_res
+
+
+def group_packages_into_tasks(pkgs):
+ """Given a list of packages (with .tasks) group them per each
+ task and perform necessary customizations stored in .tasks
+ """
+ # Time to take care about packages and tasks
+ # Unroll pkgs into a collection of pkgs per known task
+ tasks = {}
+ for pkg in pkgs:
+ # Lets just create deepcopies with tune-ups for each task
+ for itask, (task, fields) in enumerate(pkg.tasks.iteritems()):
+ pkg_ = deepcopy(pkg)
+ pkg_.update(fields)
+
+ # Perform string completions and removals
+ for k,v in pkg_.iteritems():
+ pkg_[k] = v % pkg_
+ if v is None or not len(v.strip()):
+ pkg_.pop(k)
+
+ # Sort the fields according to FIELDS_ORDER. Unfortunately
+ # silly Deb822* cannot create from list of tuples, so will do
+ # manually
+ pkg__ = deb822.Deb822()
+ for k,v in sorted(pkg_.items(),
+ cmp=lambda x, y:
+ key_prefix_compare(x, y, order=FIELDS_ORDER)):
+ pkg__[k] = v
+
+ # Move Pkg-source/name into attributes
+ pkg__.source = pkg__.pop('Pkg-Source')
+ pkg__.name = pkg__.pop('Pkg-Name')
+ # Store the action taken on the package for later on actions
+ for f in PKG_FIELDS:
+ if f in pkg__:
+ pkg__.action = f
+ break
+
+ tasks[task] = tasks.get(task, []) + [pkg__]
+ verbose(4, "Grouped %d packages into %d tasks: %s" %
+ (len(pkgs), len(tasks), ', '.join(tasks.keys())))
+ return tasks
+
+def inject_tasks(tasks, config):
+ # Now go through task files and replace/add entries
+ for task, pkgs in tasks.iteritems():
+ verbose(2, "Task %s with %d packages" % (task, len(pkgs)))
+ blend, puretask = task.split('/')
+ taskfile = expanduser(join(config.get(blend, 'path'), 'tasks', puretask))
+
+ # Load the file
+ stats = dict(Added=[], Modified=[])
+ for pkg in pkgs:
+ msgs = {'Name': pkg.name.strip(), 'Action': None}
+
+ # Create a copy of the pkg with only valid tasks
+ # fields:
+ # TODO: make it configurable?
+ pkg = deepcopy(pkg)
+ for k in pkg:
+ if prefix_index(k, BLENDS_FIELDS_PREFIXES,
+ strict=False, default=None) is None:
+ pkg.pop(k) # remove it from becoming present in
+ # the taskfile
+
+ # Find either it is known to the task file already
+
+ # Load entirely so we could simply manipulate
+ entries = open(taskfile).readlines()
+ known = False
+ # We need to search by name and by source
+ # We need to search for every possible type of dependency
+ regexp_str = '^ *(%s) *: *(%s) *$' \
+ % ('|'.join(PKG_FIELDS),
+ '|'.join((pkg.name, pkg.source)).replace('+', '\+'))
+ verbose(4, "Searching for presence in %s using regexp: '%s'"
+ % (taskfile, regexp_str))
+ regexp = re.compile(regexp_str, re.I)
+ for istart, e in enumerate(entries):
+ if regexp.search(e):
+ verbose(4, "Found %s in position %i: %s" %
+ (pkg.name, istart, e.rstrip()))
+ known = True
+ break
+
+ descr = ' ; Added by %s %s. [Please note here if modified manually]\n' % \
+ (__prog__, __version__)
+
+ entry = pkg.dump()
+ # Replace existing entry?
+ if known:
+ # TODO: Check if previous copy does not have our preceding comment
+ # Find the previous end
+ icount = 1
+ try:
+ while entries[istart+icount].strip() != '':
+ icount += 1
+ except IndexError, e:
+ pass # if we go beyond
+
+ # Lets not change file without necessity, if entry is identical --
+ # do nothing
+ old_entry = entries[istart:istart+icount]
+
+ if u''.join(old_entry) == entry:
+ # no changes -- just go to the next one
+ continue
+ else: # Rewrite the entry
+ if __prog__ in entries[istart-1]:
+ istart -= 1
+ icount += 2
+ if 'remove' != pkg.action:
+ entry = descr + entry
+ msgs['Action'] = 'Changed'
+ else:
+ while entries[istart-1].strip() == '':
+ istart -=1
+ icount +=2
+ entry = ''
+ msgs['Action'] = 'Removed'
+ entries_prior = entries[:istart]
+ entries_post = entries[istart+icount:]
+ elif not 'remove' == pkg.action: # or Append one
+ msgs['Action'] = 'Added'
+ entries_prior = entries
+ entry = descr + entry
+ entries_post = []
+ # could be as simple as
+ # Lets do 'in full' for consistent handling of empty lines
+ # around
+ #output = '\n%s%s' % (descr, pkg.dump(),)
+ #open(taskfile, 'a').write(output)
+
+ if msgs['Action']:
+ # Prepare for dumping
+ # Prune spaces before
+ while len(entries_prior) and entries_prior[-1].strip() == '':
+ entries_prior = entries_prior[:-1]
+ if len(entries_prior) and not entries_prior[-1].endswith('\n'):
+ entries_prior[-1] += '\n' # assure present trailing newline
+ # Prune spaces after
+ while len(entries_post) and entries_post[0].strip() == '':
+ entries_post = entries_post[1:]
+ if len(entries_post) and len(entry):
+ # only then trailing empty line
+ entry += '\n'
+ output = ''.join(entries_prior + [ '\n' + entry ] + entries_post)
+ open(taskfile, 'w').write(output) # then only overwrite
+
+ verbose(3, "%(Action)s %(Name)s" % msgs)
+
+