littlefs/scripts/changeprefix.py
Christopher Haster f0cc1db793 Tweaked changeprefix.py to not rename dir component in paths
This wasn't implemented correctly anyways, as it would need to recursively
rename directories that may not exist. Things would also get a bit
complicated if only some files in a directory were renamed.

Doable, but not needed for our use case.

For now just ignore any directory components. Though this may be worth
changing if the source directory structure becomes more complicated in
the future (maybe with a -r/--recursive flag?).
2023-04-19 18:33:47 -05:00

182 lines
5.3 KiB
Python
Executable File

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