#!/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 ""}, using first remote') remote = git('remote', repo=repo).strip().splitlines()[0].strip() if not remote: raise SystemError(f'Repo {repo or ""} 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()