119 lines
4.4 KiB
Python
119 lines
4.4 KiB
Python
#!/usr/bin/env python3
|
|
|
|
import shutil
|
|
import textwrap
|
|
import subprocess
|
|
import sys
|
|
import fnmatch
|
|
import warnings
|
|
import filecmp
|
|
from pathlib import Path
|
|
|
|
import click
|
|
|
|
FILENAME_PATTERNS = ['*.svg', '*.png', '*.jpg', '*.pdf', '*.eps']
|
|
|
|
|
|
def git(*args, repo=None):
|
|
cmd = ['git']
|
|
if repo:
|
|
cmd += ['-C', str(repo)]
|
|
proc = subprocess.run([*cmd, *args], check=True, capture_output=True, text=True)
|
|
return proc.stdout + proc.stderr
|
|
|
|
def remote_url(repo=None):
|
|
branch = 'unknown'
|
|
try:
|
|
branch = git('branch', '--show-current', repo=repo).strip()
|
|
remote = git('config', f'branch.{branch}.remote', repo=repo).strip()
|
|
except subprocess.CalledProcessError:
|
|
warnings.warn(f'Cannot identify git remote for branch {branch} in repo {repo or "<this repo>"}, using first remote')
|
|
|
|
remote = git('remote', repo=repo).strip().splitlines()[0].strip()
|
|
if not remote:
|
|
raise SystemError(f'Repo {repo or "<this repo>"} has no remotes configured.')
|
|
|
|
return git('remote', 'get-url', remote, repo=repo).strip()
|
|
|
|
def paths(s):
|
|
return [Path(line.strip()) for line in s.strip().splitlines()]
|
|
|
|
def tex_escape(s):
|
|
s = str(s)
|
|
s = s.replace('_', r'\_')
|
|
return s
|
|
|
|
|
|
@click.command()
|
|
@click.argument('figure_dir', default='.', type=click.Path(exists=True, file_okay=False, path_type=Path))
|
|
def cli(figure_dir):
|
|
figure_dir = figure_dir.resolve()
|
|
figure_files = set(fn.name for fn in figure_dir.iterdir()
|
|
if fn.is_file() and any(fnmatch.fnmatch(fn.name.lower(), pat) for pat in FILENAME_PATTERNS))
|
|
|
|
submodules = [p for p in paths(git('submodule', 'foreach', '--quiet', 'pwd'))
|
|
if p.parent == figure_dir]
|
|
|
|
repo_matches = {}
|
|
for mod in submodules:
|
|
for fn in paths(git('ls-tree', '-r', 'HEAD', '--name-only', '--full-tree', repo=mod)):
|
|
if fn.name in figure_files:
|
|
if fn.name in repo_matches:
|
|
prev_mod, prev_fn = repo_matches[fn.name]
|
|
warnings.warn(f'Ambiguous match for {fn.name}: found {mod / fn} in addition to previous match of {prev_mod / prev_fn}.')
|
|
else:
|
|
repo_matches[fn.name] = (mod, fn)
|
|
|
|
meta_file = lambda suffix: figure_dir / (fig_fn + suffix)
|
|
|
|
for fig_fn, (mod, mod_fn) in repo_matches.items():
|
|
fig_file = figure_dir / fig_fn
|
|
mod_file = mod / mod_fn
|
|
scale = ""
|
|
if filecmp.cmp(fig_file, mod_file):
|
|
print(f'{fig_fn} is up to date, updating metadata.')
|
|
else:
|
|
shutil.copy(mod_file, fig_file)
|
|
if fig_file.suffix.lower() in ('.png', '.jpg'):
|
|
subprocess.run(['magick', fig_file,
|
|
'-resize', '1000x1000>',
|
|
figure_dir / f'scaled-{fig_file.name}'],
|
|
check=True)
|
|
scale = "scaled down for preview"
|
|
print(f'Scaled {fig_fn} for preview')
|
|
|
|
elif fig_file.suffix.lower() == '.svg':
|
|
subprocess.run(['inkscape', f'--export-filename={fig_file.with_suffix(".eps")}', fig_file],
|
|
check=True)
|
|
print(f'Rendered {fig_fn} to EPS')
|
|
|
|
print(f'Updated {fig_fn} from {mod_fn}.')
|
|
|
|
repo_url = remote_url(repo=mod)
|
|
|
|
url_prefix = repo_url.replace('git@git.jaseg.de:', 'https://git.jaseg.de/')
|
|
repo_name = repo_url.replace('git@git.jaseg.de:', '').replace('https://git.jaseg.de/', '')
|
|
git_rev = git('rev-parse', 'HEAD', repo=mod).strip()
|
|
cgit_url = f'{url_prefix}/plain/{mod_fn}?h={git_rev}'
|
|
|
|
git_tag = git('describe', '--always', '--tags', repo=mod).strip()
|
|
|
|
state = r'\camerareadygraphics' if 'camera-ready' in fig_fn or 'final' in fig_fn else r'\draftgraphics'
|
|
meta_file('.latex_meta').write_text(textwrap.dedent(fr'''
|
|
\def\resourcestate{{{state}}}
|
|
\def\resourcescale{{{scale}}}
|
|
\def\resourceurl{{{tex_escape(cgit_url)}}}
|
|
\def\resourcerev{{{tex_escape(git_tag)}}}
|
|
\def\resourcerepo{{{tex_escape(repo_name)}}}
|
|
\def\resourcepath{{{tex_escape(mod_fn)}}}
|
|
''').strip())
|
|
|
|
unmatched = figure_files - set(repo_matches.keys())
|
|
if unmatched:
|
|
print()
|
|
print('Unmatched files:', file=sys.stderr)
|
|
for fn in unmatched:
|
|
print(fn, file=sys.stderr)
|
|
|
|
if __name__ == '__main__':
|
|
cli()
|