forked from Imagelibrary/littlefs
This seems like a more fitting name now that this script has evolved
into more of a general purpose high-level CSV tool.
Unfortunately this does conflict with the standard csv module in Python,
breaking every script that imports csv (which is most of them).
Fortunately, Python is flexible enough to let us remove the current
directory before imports with a bit of an ugly hack:
# prevent local imports
__import__('sys').path.pop(0)
These scripts are intended to be standalone anyways, so this is probably
a good pattern to adopt.
184 lines
5.4 KiB
Python
Executable File
184 lines
5.4 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
|
|
#
|
|
|
|
# prevent local imports
|
|
__import__('sys').path.pop(0)
|
|
|
|
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 'r' in mode:
|
|
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, _ = changeprefix(from_prefix, to_prefix, from_path)
|
|
|
|
# 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}))
|