#!/usr/bin/env python3 # # Copyright (c) 2017 Jon Turney # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. # # # Move a given source archive to src/ (assuming it is indentical in x86/ and # x86_64/) and adjust hints appropriately. # (XXX: could probably be extended to move to noarch/ if not source, as well) # import argparse import copy import os import re import sys from . import common_constants from . import hint from . import utils binary_only_hints = ['requires', 'depends', 'obsoletes', 'external-source'] # # # def hint_file_write(fn, hints): with open(fn, 'w') as f: for k, v in hints.items(): print("%s: %s" % (k, v), file=f) # # # def invent_sdesc(path, vr): for (dirpath, _subdirs, files) in os.walk(path): # debuginfo packages never have a good sdesc if 'debuginfo' in dirpath: continue # but just pick the sdesc from first sub-package which has one ... for f in files: if re.match('^.*-' + re.escape(vr) + '.hint$', f): hints = hint.hint_file_parse(os.path.join(dirpath, f), hint.pvr) if 'sdesc' in hints: sdesc = hints['sdesc'] # ... which doesn't contain 'Obsoleted' if 'Obsoleted' in sdesc: continue # remove anything inside parentheses at the end of quoted # sdesc sdesc = re.sub(r'"(.*)"', r'\1', sdesc) sdesc = re.sub(r'(\(.*?\))$', '', sdesc) sdesc = sdesc.strip() sdesc = '"' + sdesc + '"' return sdesc return None # # # def dedup(archive, relarea): # split path and filename (path, filename) = os.path.split(archive) # parse tarfile name match = re.match(r'^(.+?)-(\d.*)-src\.tar' + common_constants.PACKAGE_COMPRESSIONS_RE + r'$', filename) if not match: print('tarfile name %s does not meet expectations' % (filename)) sys.exit(1) p = match.group(1) vr = match.group(2) ext = match.group(3) # compute filenames to_filename = p + '-src-' + vr + '.tar.' + ext hint_filename = p + '-' + vr + '.hint' to_hint_filename = p + '-src-' + vr + '.hint' # read hints for both arches hints = {} for arch in ['x86', 'x86_64']: hint_pathname = os.path.join(relarea, arch, path, hint_filename) if not os.path.exists(hint_pathname): print('%s not found' % (hint_pathname)) return 1 hints[arch] = hint.hint_file_parse(hint_pathname, hint.pvr) # remove hints which only have meaning for binary packages # # (requires: tends to have libgcc1 more often on x86, so otherwise this # would cause spurious differences between hints to be reported) for h in binary_only_hints: if h in hints[arch]: del hints[arch][h] if hints['x86'] != hints['x86_64']: print('hints for %s-%s differ between arches' % (p, vr)) return 1 if ('skip' in hints['x86']) and (len(hints['x86']) == 1): print('hints for %s-%s is skip: only' % (p, vr)) hints['x86']['category'] = '' # if hint only contains skip:, try to come up with a plausible sdesc sdesc = invent_sdesc(os.path.join(relarea, 'x86', path), vr) if sdesc: print('suggested sdesc is %s' % (sdesc)) hints['x86']['sdesc'] = sdesc if 'sdesc' not in hints['x86']: print('hints for %s-%s has no sdesc:' % (p, vr)) return 1 # ensure target directory exists utils.makedirs(os.path.join(relarea, 'src', path, p + '-src')) # write .hint file for new -src package src_hints = copy.copy(hints['x86']) if 'source' not in src_hints['sdesc']: sdesc = re.sub(r'"(.*)"', r'\1', src_hints['sdesc']) sdesc += ' (source code)' src_hints['sdesc'] = '"' + sdesc + '"' if 'Source' not in src_hints['category']: src_hints['category'] = src_hints['category'] + ' Source' if 'parse-warnings' in src_hints: del src_hints['parse-warnings'] to_hint_pathname = os.path.join(relarea, 'src', path, p + '-src', to_hint_filename) print('writing %s' % (to_hint_pathname)) hint_file_write(to_hint_pathname, src_hints) # move the src files to src/ for arch in ['x86', 'x86_64']: print('%s -> %s' % (os.path.join(relarea, arch, path, filename), os.path.join(relarea, 'src', path, p + '-src', to_filename))) os.rename(os.path.join(relarea, arch, path, filename), os.path.join(relarea, 'src', path, p + '-src', to_filename)) # adjust external-source in .hint for all subpackages for arch in ['x86', 'x86_64']: for (dirpath, _subdirs, files) in os.walk(os.path.join(relarea, arch, path)): subpkg = os.path.basename(dirpath) filename = subpkg + '-' + vr + '.hint' if filename in files: hint_pathname = os.path.join(dirpath, filename) hints = hint.hint_file_parse(hint_pathname, hint.pvr) if 'parse-warnings' in hints: del hints['parse-warnings'] if ('skip' in hints): # p was source only, so no package remains print('removing %s' % (hint_pathname)) os.remove(hint_pathname) elif ('external-source' not in hints) or (hints['external-source'] == p): hints['external-source'] = p + '-src' print('writing %s' % (hint_pathname)) hint_file_write(hint_pathname, hints) return 0 # # # def main(): relarea_default = common_constants.FTP parser = argparse.ArgumentParser(description='Source package deduplicator') parser.add_argument('archive', metavar='ARCHIVE', nargs=1, help="source archive to deduplicate") parser.add_argument('--releasearea', action='store', metavar='DIR', help="release directory (default: " + relarea_default + ")", default=relarea_default, dest='rel_area') (args) = parser.parse_args() return dedup(args.archive[0], args.rel_area) # # # if __name__ == "__main__": sys.exit(main())