#!/usr/bin/env python3 import argparse import sys from pathlib import Path def handle_file(path): ''' Return a tuple of (header, path) for the file at path. If the file does not have a header, the header is the empty string. ''' with open(path) as fd: header = fd.readline() if header.startswith('! '): return header, path else: return '', path def merge(dest, files): ''' Merge the content of all files into the file dest. The first line of each file is an optional section header in the form e.g. ! model = keycodes Where two sections have identical headers, the second header is skipped. Special case are header-less files which we store with the empty string as header, these need to get written out first. ''' def sort_basename(path): return path.name # sort the file list by basename files.sort(key=sort_basename) # pre-populate with the empty header so it's the first one to be written # out. We use section_names to keep the same order as we get the files # passed in (superfluous with python 3.6+ since the dict keeps the # insertion order anyway). sections = {'': []} section_names = [''] for path in files: # files may exist in srcdir or builddir, depending whether they're # generated header, path = handle_file(path) paths = sections.get(header, []) paths.append(path) sections[header] = paths if header not in section_names: section_names.append(header) for header in section_names: if header: dest.write('\n') dest.write(header) for f in sections[header]: with open(f) as fd: if header: fd.readline() # drop the header dest.write(fd.read()) if __name__ == '__main__': parser = argparse.ArgumentParser('rules file merge script') parser.add_argument('--dest', type=str, default=None) parser.add_argument('--srcdir', type=str) parser.add_argument('--builddir', type=str) parser.add_argument('files', nargs='+', type=str) ns = parser.parse_args() if ns.dest is None: dest = sys.stdout else: dest = ns.dest with dest or open(dest, 'w') as fd: basename = Path(sys.argv[0]).name fd.write('// DO NOT EDIT THIS FILE - IT WAS AUTOGENERATED BY {} FROM rules/*.part\n'.format(basename)) fd.write('//\n') def find_file(f): if ns.builddir: path = Path(ns.builddir) / f if path.exists(): return path if ns.srcdir: path = Path(ns.srcdir) / f if path.exists(): return path return Path(f) merge(fd, [find_file(f) for f in ns.files])