mirror of
https://github.com/JakeHillion/scx.git
synced 2024-11-29 20:50:22 +00:00
289 lines
8.1 KiB
Python
Executable File
289 lines
8.1 KiB
Python
Executable File
#!/usr/bin/python
|
|
|
|
import argparse
|
|
import json
|
|
import os
|
|
import re
|
|
import sys
|
|
import subprocess
|
|
|
|
verbose = False
|
|
|
|
def warn(line):
|
|
print('[WARN] ' + line, file=sys.stderr)
|
|
|
|
def err(line):
|
|
raise Exception(line)
|
|
|
|
def dbg(line):
|
|
if verbose:
|
|
print('[DBG] ' + line, file=sys.stderr)
|
|
|
|
def underline(string):
|
|
return f'\033[4m{string}\033[0m'
|
|
|
|
def do_meson_ver(new_ver):
|
|
path = 'meson.build'
|
|
with open(path, 'r') as f:
|
|
lines = f.readlines()
|
|
|
|
ver_lineno = -1
|
|
for lineno, line in enumerate(lines):
|
|
ver_re = r"(^.*version:\s*')([0-9.]*)('.*$)"
|
|
m = re.match(ver_re, line.rstrip())
|
|
if m:
|
|
ver_lineno = lineno
|
|
pre = m.group(1)
|
|
ver = m.group(2)
|
|
post = m.group(3)
|
|
dbg(f'[{path}:{lineno+1}] {pre}{underline(ver)}{post}')
|
|
break
|
|
|
|
if ver_lineno < 0:
|
|
err(f'[{path}] Failed to find verion')
|
|
|
|
if new_ver is None or ver == new_ver:
|
|
return ver
|
|
|
|
print(f'[{path}:{ver_lineno+1}] Updating from {ver} to {new_ver}')
|
|
lines[ver_lineno] = f'{pre}{new_ver}{post}\n'
|
|
with open(path, 'w') as f:
|
|
f.writelines(lines)
|
|
|
|
return ver
|
|
|
|
def get_rust_paths():
|
|
result = subprocess.run(['git', 'ls-files'], stdout=subprocess.PIPE)
|
|
lines = result.stdout.decode('utf-8').splitlines()
|
|
paths = []
|
|
for line in lines:
|
|
if line.endswith('Cargo.toml'):
|
|
paths.append(line)
|
|
return paths
|
|
|
|
def cargo_path_to_crate(path):
|
|
return path.split('/')[-2]
|
|
|
|
def do_rust_ver(path, new_ver):
|
|
with open(path, 'r') as f:
|
|
lines = f.readlines()
|
|
|
|
name_lineno = -1
|
|
ver_lineno = -1
|
|
name = None
|
|
ver = None
|
|
for lineno, line in enumerate(lines):
|
|
workspace_re = r'(^\s*)(\[\s*workspace\s*\])(.*$)'
|
|
name_re = r'(^\s*name\s*=\s*")(.*)(".*$)'
|
|
ver_re = r'(^\s*version\s*=\s*")(.*)(".*$)'
|
|
line = line.rstrip()
|
|
|
|
m = re.match(workspace_re, line)
|
|
if m:
|
|
dbg(f'[{path}:{lineno}] SKIP: {m.group(1)}{underline(m.group(2))}{m.group(3)}')
|
|
return None
|
|
|
|
m = re.match(name_re, line)
|
|
if m:
|
|
name_lineno = lineno
|
|
name = m.group(2)
|
|
dbg(f'[{path}:{lineno+1}] {m.group(1)}{underline(name)}{m.group(3)}')
|
|
else:
|
|
m = re.match(ver_re, line)
|
|
if m:
|
|
ver_lineno = lineno
|
|
pre = m.group(1)
|
|
ver = m.group(2)
|
|
post = m.group(3)
|
|
dbg(f'[{path}:{lineno+1}] {pre}{underline(ver)}{post}')
|
|
|
|
if name_lineno >= 0 and ver_lineno >= 0:
|
|
break
|
|
|
|
if name_lineno < 0 or ver_lineno < 0:
|
|
err(f'[{path}] Failed to find name or version')
|
|
|
|
if name != cargo_path_to_crate(path):
|
|
warn(f'[{path}:{name_lineno}] name \"{name}\" does not match the path')
|
|
|
|
if new_ver is None or ver == new_ver:
|
|
return ver
|
|
|
|
print(f'[{path}:{ver_lineno+1}] Updating from {ver} to {new_ver}')
|
|
lines[ver_lineno] = f'{pre}{new_ver}{post}\n'
|
|
with open(path, 'w') as f:
|
|
f.writelines(lines)
|
|
|
|
return ver
|
|
|
|
def do_rust_deps(path, deps, new_deps):
|
|
with open(path, 'r') as f:
|
|
lines = f.readlines()
|
|
|
|
in_dep = None
|
|
block_depth = 0
|
|
crate = None
|
|
need_write = False
|
|
|
|
for lineno, line in enumerate(lines):
|
|
line = line.rstrip()
|
|
|
|
# determine whether in a dependencies section
|
|
sect_re = r'^\s*\[([^\[\]]*)]\s*$'
|
|
m = re.match(sect_re, line)
|
|
if m:
|
|
if block_depth != 0:
|
|
err(f'[{path}:{lineno+1}] Unbalanced block_depth {block_depth}');
|
|
|
|
sect = m.group(1).strip()
|
|
if sect.endswith('dependencies'):
|
|
dbg(f'[{path}{lineno+1}] [{sect}]')
|
|
in_dep = sect
|
|
else:
|
|
in_dep = None
|
|
continue
|
|
|
|
if not in_dep:
|
|
continue
|
|
|
|
# strip and store comment
|
|
body = line
|
|
comment_re = r'(^.*)(#.*$)'
|
|
comment = ""
|
|
m = re.match(comment_re, body)
|
|
if m:
|
|
body = m.group(1)
|
|
comment = m.group(2)
|
|
|
|
if len(body.strip()) == 0:
|
|
continue
|
|
|
|
# determine the current crate
|
|
if block_depth == 0:
|
|
crate_re = r'^\s*([^=\s]*)\s*=.*$'
|
|
m = re.match(crate_re, body)
|
|
if m:
|
|
crate = m.group(1)
|
|
crate_on_line = True
|
|
else:
|
|
warn(f'[{path}:{lineno+1}] Failed to find crate name')
|
|
crate = None
|
|
else:
|
|
crate_on_line = False
|
|
|
|
# do dumb nesting depth tracking
|
|
block_depth += body.count('{') - body.count('}')
|
|
block_depth += body.count('[') - body.count(']')
|
|
|
|
if crate is None:
|
|
continue
|
|
|
|
# determine the crate version
|
|
ver = None
|
|
if crate_on_line:
|
|
ver_re = r'(^[^=].*=\s*")([^"]*)("\s*$)'
|
|
m = re.match(ver_re, body)
|
|
if m:
|
|
pre = m.group(1)
|
|
ver = m.group(2)
|
|
post = m.group(3)
|
|
if ver is None:
|
|
ver_re = r'(^.*version\s*=\s*")([^"]*)(".*$)'
|
|
m = re.match(ver_re, body)
|
|
if m:
|
|
pre = m.group(1)
|
|
ver = m.group(2)
|
|
post = m.group(3)
|
|
if ver is None:
|
|
if block_depth == 0:
|
|
warn(f'{path}:{lineno+1} no version')
|
|
continue
|
|
|
|
dbg(f'[{path}:{lineno+1}] {crate}: {pre}{underline(ver)}{post}')
|
|
|
|
# check whether the version matches
|
|
if crate in deps:
|
|
if deps[crate] != ver:
|
|
warn(f'[{path}:{lineno+1}] crate "{crate}" {ver} mismatches existing {deps[crate]}')
|
|
else:
|
|
deps[crate] = ver
|
|
|
|
if crate in new_deps:
|
|
new_ver = new_deps[crate]
|
|
if ver != new_ver:
|
|
print(f'[{path}:{lineno+1}] Updating dep {crate} = "{ver}" -> "{new_ver}"')
|
|
lines[lineno] = f'{pre}{new_ver}{post}{comment}\n'
|
|
need_write = True
|
|
|
|
crate = None
|
|
|
|
if block_depth != 0:
|
|
err(f'[{path}:{lineno+1}] Unbalanced block_depth {block_depth}');
|
|
|
|
if need_write:
|
|
with open(path, 'w') as f:
|
|
f.writelines(lines)
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(prog='version-tool.py',
|
|
description='Check and update versions. "version-tool.py > vers.json" to generate the template. Apply the edited version with "version-too.py -u vers.json"')
|
|
parser.add_argument('-u', '--update', metavar='JSON', type=str,
|
|
help='Update versions from the specified json file')
|
|
parser.add_argument('-v', '--verbose', action='store_true')
|
|
|
|
args = parser.parse_args()
|
|
|
|
global verbose
|
|
verbose = args.verbose
|
|
|
|
vers_key = '00-versions'
|
|
rust_vers_key = '01-rust-versions'
|
|
rust_deps_key = '02-rust-deps'
|
|
|
|
vers = {}
|
|
rust_vers = {}
|
|
rust_deps = {}
|
|
|
|
new_vers = {}
|
|
new_rust_vers = {}
|
|
new_rust_deps = {}
|
|
|
|
if args.update:
|
|
with open(args.update, 'r') as f:
|
|
parsed = json.loads(f.read())
|
|
new_vers = parsed[vers_key]
|
|
new_rust_vers = parsed[rust_vers_key]
|
|
new_rust_deps = parsed[rust_deps_key]
|
|
|
|
# package version
|
|
vers['meson'] = do_meson_ver(new_vers.get('meson'))
|
|
|
|
# rust crates implemented in the tree
|
|
rust_paths = get_rust_paths()
|
|
for path in rust_paths:
|
|
name = cargo_path_to_crate(path)
|
|
ver = do_rust_ver(path, new_rust_vers.get(name))
|
|
if ver:
|
|
rust_vers[name] = ver
|
|
|
|
# crates implemented in the tree are included as deps by default
|
|
rust_deps.update(rust_vers)
|
|
new_rust_deps.update(new_rust_vers)
|
|
|
|
# rust dependencies
|
|
for path in rust_paths:
|
|
do_rust_deps(path, rust_deps, new_rust_deps)
|
|
|
|
# if not updating, print out what's read
|
|
if args.update is None:
|
|
for crate in rust_vers:
|
|
rust_deps.pop(crate)
|
|
|
|
manifest = { vers_key: vers,
|
|
rust_vers_key: rust_vers,
|
|
rust_deps_key: rust_deps }
|
|
|
|
print(json.dumps(manifest, sort_keys=True, indent=4))
|
|
|
|
main()
|