#!/usr/bin/env python3 # # Change prefixes in files/filenames. Useful for creating different versions # of a codebase that don't conflict at compile time. # # Example: # $ ./scripts/changeprefix.py lfs lfs3 # # Copyright (c) 2022, The littlefs authors. # Copyright (c) 2019, Arm Limited. All rights reserved. # SPDX-License-Identifier: BSD-3-Clause # import glob import itertools import os import os.path import re import shlex import shutil import subprocess import tempfile GIT_PATH = ['git'] def openio(path, mode='r', buffering=-1): # allow '-' for stdin/stdout if path == '-': if mode == 'r': return os.fdopen(os.dup(sys.stdin.fileno()), mode, buffering) else: return os.fdopen(os.dup(sys.stdout.fileno()), mode, buffering) else: return open(path, mode, buffering) def changeprefix(from_prefix, to_prefix, line): line, count1 = re.subn( '\\b'+from_prefix, to_prefix, line) line, count2 = re.subn( '\\b'+from_prefix.upper(), to_prefix.upper(), line) line, count3 = re.subn( '\\B-D'+from_prefix.upper(), '-D'+to_prefix.upper(), line) return line, count1+count2+count3 def changefile(from_prefix, to_prefix, from_path, to_path, *, no_replacements=False): # rename any prefixes in file count = 0 # create a temporary file to avoid overwriting ourself if from_path == to_path and to_path != '-': to_path_temp = tempfile.NamedTemporaryFile('w', delete=False) to_path = to_path_temp.name else: to_path_temp = None with openio(from_path) as from_f: with openio(to_path, 'w') as to_f: for line in from_f: if not no_replacements: line, n = changeprefix(from_prefix, to_prefix, line) count += n to_f.write(line) if from_path != '-' and to_path != '-': shutil.copystat(from_path, to_path) if to_path_temp: os.rename(to_path, from_path) elif from_path != '-': os.remove(from_path) # Summary print('%s: %d replacements' % ( '%s -> %s' % (from_path, to_path) if not to_path_temp else from_path, count)) def main(from_prefix, to_prefix, paths=[], *, verbose=False, output=None, no_replacements=False, no_renames=False, git=False, no_stage=False, git_path=GIT_PATH): if not paths: if git: cmd = git_path + ['ls-tree', '-r', '--name-only', 'HEAD'] if verbose: print(' '.join(shlex.quote(c) for c in cmd)) paths = subprocess.check_output(cmd, encoding='utf8').split() else: print('no paths?', file=sys.stderr) sys.exit(1) for from_path in paths: # rename filename? if output: to_path = output elif no_renames: to_path = from_path else: to_path = os.path.join( os.path.dirname(from_path), changeprefix(from_prefix, to_prefix, os.path.basename(from_path))[0]) # rename contents changefile(from_prefix, to_prefix, from_path, to_path, no_replacements=no_replacements) # stage? if git and not no_stage: if from_path != to_path: cmd = git_path + ['rm', '-q', from_path] if verbose: print(' '.join(shlex.quote(c) for c in cmd)) subprocess.check_call(cmd) cmd = git_path + ['add', to_path] if verbose: print(' '.join(shlex.quote(c) for c in cmd)) subprocess.check_call(cmd) if __name__ == "__main__": import argparse import sys parser = argparse.ArgumentParser( description="Change prefixes in files/filenames. Useful for creating " "different versions of a codebase that don't conflict at compile " "time.", allow_abbrev=False) parser.add_argument( 'from_prefix', help="Prefix to replace.") parser.add_argument( 'to_prefix', help="Prefix to replace with.") parser.add_argument( 'paths', nargs='*', help="Files to operate on.") parser.add_argument( '-v', '--verbose', action='store_true', help="Output commands that run behind the scenes.") parser.add_argument( '-o', '--output', help="Output file.") parser.add_argument( '-N', '--no-replacements', action='store_true', help="Don't change prefixes in files") parser.add_argument( '-R', '--no-renames', action='store_true', help="Don't rename files") parser.add_argument( '--git', action='store_true', help="Use git to find/update files.") parser.add_argument( '--no-stage', action='store_true', help="Don't stage changes with git.") parser.add_argument( '--git-path', type=lambda x: x.split(), default=GIT_PATH, help="Path to git executable, may include flags. " "Defaults to %r." % GIT_PATH) sys.exit(main(**{k: v for k, v in vars(parser.parse_intermixed_args()).items() if v is not None}))