#!/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])