Initial checkin. Base working version.
This commit is contained in:
commit
9ff59dd2f1
|
@ -0,0 +1,226 @@
|
|||
# Created by https://www.toptal.com/developers/gitignore/api/emacs,python
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=emacs,python
|
||||
|
||||
### Emacs ###
|
||||
# -*- mode: gitignore; -*-
|
||||
*~
|
||||
\#*\#
|
||||
/.emacs.desktop
|
||||
/.emacs.desktop.lock
|
||||
*.elc
|
||||
auto-save-list
|
||||
tramp
|
||||
.\#*
|
||||
|
||||
# Org-mode
|
||||
.org-id-locations
|
||||
*_archive
|
||||
|
||||
# flymake-mode
|
||||
*_flymake.*
|
||||
|
||||
# eshell files
|
||||
/eshell/history
|
||||
/eshell/lastdir
|
||||
|
||||
# elpa packages
|
||||
/elpa/
|
||||
|
||||
# reftex files
|
||||
*.rel
|
||||
|
||||
# AUCTeX auto folder
|
||||
/auto/
|
||||
|
||||
# cask packages
|
||||
.cask/
|
||||
dist/
|
||||
|
||||
# Flycheck
|
||||
flycheck_*.el
|
||||
|
||||
# server auth directory
|
||||
/server/
|
||||
|
||||
# projectiles files
|
||||
.projectile
|
||||
|
||||
# directory configuration
|
||||
.dir-locals.el
|
||||
|
||||
# network security
|
||||
/network-security.data
|
||||
|
||||
|
||||
### Python ###
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
cover/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
.pybuilder/
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
# For a library or package, you might want to ignore these files since the code is
|
||||
# intended to run in multiple environments; otherwise, check them in:
|
||||
# .python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# poetry
|
||||
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
||||
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||
# commonly ignored for libraries.
|
||||
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
||||
#poetry.lock
|
||||
|
||||
# pdm
|
||||
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
||||
#pdm.lock
|
||||
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
||||
# in version control.
|
||||
# https://pdm.fming.dev/#use-with-ide
|
||||
.pdm.toml
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# pytype static type analyzer
|
||||
.pytype/
|
||||
|
||||
# Cython debug symbols
|
||||
cython_debug/
|
||||
|
||||
# PyCharm
|
||||
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
||||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
#.idea/
|
||||
|
||||
### Python Patch ###
|
||||
# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration
|
||||
poetry.toml
|
||||
|
||||
# ruff
|
||||
.ruff_cache/
|
||||
|
||||
# LSP config files
|
||||
pyrightconfig.json
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/emacs,python
|
|
@ -0,0 +1,15 @@
|
|||
No Nazis
|
||||
|
||||
|
||||
Otherwise:
|
||||
|
||||
|
||||
|
||||
Copyright (c) 2023
|
||||
Aldercone Studio
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY Aldercone Studio “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL [Name of Organisation] BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -0,0 +1,26 @@
|
|||
#
|
||||
#
|
||||
#
|
||||
"""
|
||||
HECKIMP
|
||||
|
||||
A tool to rename and rebrand Gnu Image Manipulation Program from source.
|
||||
|
||||
Brought to you by Aldercone Studio
|
||||
https://heckin.technology/AlderconeStudio/heckimp/
|
||||
|
||||
"""
|
||||
|
||||
|
||||
__version__ = "0.1.0"
|
||||
|
||||
LOGO="""
|
||||
aldercone
|
||||
▌ ▌ ▗
|
||||
▛▀▖▞▀▖▞▀▖▌▗▘▄ ▛▚▀▖▛▀▖
|
||||
▌ ▌▛▀ ▌ ▖▛▚ ▐ ▌▐ ▌▙▄▘
|
||||
▘ ▘▝▀▘▝▀ ▘ ▘▀▘▘▝ ▘▌
|
||||
"""
|
||||
|
||||
|
||||
ANNULUS = """M 116.63112 63.091239 C 87.273549 63.809372 39.29769 97.201192 38.448816 122.99818 C 37.404048 154.74832 76.218915 173.98333 120.81226 162.44331 C 152.15791 154.33157 154.42018 88.62447 133.17275 68.309009 C 129.18886 64.49986 123.40595 62.925517 116.63112 63.091239 z M 97.860177 76.918819 C 105.16897 76.821912 111.75814 79.563777 116.37998 85.410807 C 128.70488 101.0029 111.56781 150.92535 93.210331 149.80068 C 74.852841 148.67601 49.394526 116.36811 61.094979 99.831632 C 71.498378 85.128312 85.678846 77.080331 97.860177 76.918819 z"""
|
|
@ -0,0 +1,304 @@
|
|||
"""
|
||||
Heckweasel: Rename GnuImp
|
||||
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import datetime
|
||||
import logging
|
||||
import os
|
||||
import pprint
|
||||
import re
|
||||
import shutil
|
||||
import sys
|
||||
import traceback
|
||||
import yaml
|
||||
|
||||
import tqdm # type: ignore
|
||||
|
||||
from pathlib import Path
|
||||
from typing import Sequence, Union
|
||||
|
||||
from PIL import Image
|
||||
|
||||
from . import (ANNULUS,
|
||||
LOGO,
|
||||
__version__)
|
||||
from .gnuimpregexp import (CONTENTS_EXCEPTIONS,
|
||||
CONTENTS_REGEXP,
|
||||
DEBUG_REGEXP,
|
||||
FILENAME_EXCEPTIONS,
|
||||
FILENAME_REGEXP,
|
||||
ICON_REGEXP,
|
||||
LOGO_REGEXP,
|
||||
MASCOT_REGEXP,
|
||||
MASCOT_SCALABLE_REGEXP,
|
||||
SCALABLE_REGEXP,
|
||||
SKIP_PROCESSING,
|
||||
SPLASH_REGEXP,
|
||||
MAKE_DIRECTORIES)
|
||||
from .misc import (TqdmLoggingHandler,
|
||||
is_binary,
|
||||
find_file,
|
||||
reverse_domain,
|
||||
PROBABLY_BINARY_EXTENSIONS
|
||||
)
|
||||
from .regexptools import (Replacement,
|
||||
apply_regexps,
|
||||
one_matches,
|
||||
process_text_file)
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
####
|
||||
# TODO
|
||||
#
|
||||
# ** wilber.ico and wilber.bmp in windows [we can make these from the input pngs and output them as mascot.bmp/ico]
|
||||
# * README - need to ttalk about canvas shadow, formats and styles for icon.s etc.
|
||||
|
||||
####
|
||||
# Later TODO
|
||||
#
|
||||
# * Add a 'additional files' config, like for installing brushes and such as part of the package. We'd have to patch
|
||||
# the appropirate meson files to do it.
|
||||
# ** we might want to allow color vs. symbolic icons
|
||||
# * impliment versioning somehow ?
|
||||
# * Fixing various urls, dialogs, etc. (also the text wiggle in about, basically sort of bury the original stuff)
|
||||
# * We should fix the README, like make a README generated
|
||||
# * We should fix help menu
|
||||
# * We should fix references to URLs in the software itself in various places
|
||||
# * It'd be cool to have a way to apply certain regex by filename instead of globally
|
||||
# * probably disable check for updates too
|
||||
# * fix script-fu, python plugins, etc. There's a ton of broken ones we'll have to fix. Something with the libarry not
|
||||
# being installed (these are broken upstream too, or due to my python setup maybe?)
|
||||
#
|
||||
# We flatten
|
||||
# icon
|
||||
# image
|
||||
# menu icons
|
||||
# mascot
|
||||
# into one set of files, but we could break these out a bit; we could also figure out how to autogenerate the
|
||||
# canvas shadow curves from the icon svg but this might be a bit far afield.
|
||||
#
|
||||
|
||||
def setup_logging(verbose:bool=False, quiet:bool=False, logfile:Union[Path, str, None]=None) -> None:
|
||||
"""
|
||||
Configure logging based on some flags.
|
||||
"""
|
||||
# Setup Tqdm handler
|
||||
logger.setLevel(logging.DEBUG)
|
||||
h = TqdmLoggingHandler()
|
||||
if verbose:
|
||||
f = logging.Formatter('%(asctime)s %(module)-12s %(levelname)-8s %(message)s')
|
||||
h.setLevel(logging.DEBUG)
|
||||
h.setFormatter(f)
|
||||
elif quiet:
|
||||
f = logging.Formatter('%(levelname)-8s %(message)s')
|
||||
h.setLevel(logging.CRITICAL)
|
||||
h.setFormatter(f)
|
||||
else:
|
||||
f = logging.Formatter('%(levelname)-8s %(message)s')
|
||||
h.setLevel(logging.INFO)
|
||||
h.setFormatter(f)
|
||||
logger.addHandler(h)
|
||||
|
||||
# setup logfile if specified
|
||||
if logfile:
|
||||
lf = logging.FileHandler(logfile)
|
||||
lf.setLevel(logging.DEBUG)
|
||||
lf.setFormatter(logging.Formatter('%(asctime)s %(module)-12s %(levelname)-8s %(message)s'))
|
||||
logger.addHandler(lf)
|
||||
|
||||
|
||||
def parse_args(args: Sequence[str]) -> argparse.Namespace:
|
||||
"""
|
||||
Parse command-line arguments.
|
||||
"""
|
||||
parser = argparse.ArgumentParser(prog="heckimp",
|
||||
description="Process clean GnuImp source tree into rebranded fork.")
|
||||
|
||||
parser.add_argument("-v", "--verbose", action="store_true", help="Show more verbosity but only a little.") # done
|
||||
parser.add_argument("-q", "--quiet", action="store_true", help="Show less innformation like not every filename.") # done
|
||||
parser.add_argument("--nogit", action="store_true", help="Don't do git stuff at the end.") # done
|
||||
|
||||
# Disabling these for now because it complicates filename transforms. We can work out better filename transform logic later and support this.
|
||||
# For now you just need the checkout as 'gimp' and the output will be the filename.
|
||||
# parser.add_argument("-i", "--input", type=Path, default="gimp", help="Force input tree, default is original codebase name.")
|
||||
# parser.add_argument("-o", "--output", type=str, help="Force output tree. Default is the app's name.", default="")
|
||||
|
||||
parser.add_argument("-c", "--config", type=Path, default=Path("heckimp-pika/pika.yaml"), help="Specify configuration file.", required=True) # done
|
||||
parser.add_argument("-p", "--paths", default=["."], nargs="+", help="Specify paths to look for images and stuff.") # done
|
||||
parser.add_argument("--dump", action="store_true", help="Don't excute, just dump configuration and replacement strings (for debugging purposes).") # done
|
||||
parser.add_argument("--log", type=str, default="", help="Write logs to specified file.") # done!
|
||||
|
||||
parser.add_argument("--no-progress", dest='no_progress', action="store_true", help="Don't show progress bar.") # done
|
||||
|
||||
# parser.add_argument("--simple", action="store_true", help="Simplify output (no progress bars and similar).") # fixme later [dependency loop]
|
||||
# parser.add_argument("--build", action="store_true", help="Also execute the build process with Meson and Ninja.") #Fixme maybe
|
||||
|
||||
return parser.parse_args(args)
|
||||
|
||||
|
||||
def heckimp(args: Sequence[str]):
|
||||
print(LOGO)
|
||||
pargs = parse_args(args[1:])
|
||||
setup_logging(pargs.verbose, pargs.quiet, pargs.log)
|
||||
|
||||
inconfig = {}
|
||||
with pargs.config.open() as conffile:
|
||||
inconfig = yaml.safe_load(conffile)
|
||||
|
||||
searchpaths = set([Path(path) for path in pargs.paths])
|
||||
searchpaths.add(pargs.config.parent)
|
||||
outdir = Path(inconfig['name'].lower())
|
||||
|
||||
if 'shadow' in inconfig:
|
||||
shadow = find_file(inconfig['shadow'], searchpaths).read_text().replace('\n', ' ')
|
||||
else:
|
||||
shadow = ANNULUS
|
||||
|
||||
derivedconfig={
|
||||
# Derived variables from config
|
||||
"longname": inconfig['name'].upper(),
|
||||
"expandedname": inconfig['expansion'],
|
||||
"symname": inconfig['name'].capitalize(),
|
||||
"filename": inconfig['name'].lower(),
|
||||
"ufilename": inconfig['name'].upper(),
|
||||
"backdomain": reverse_domain(inconfig['url']),
|
||||
"baseurl": inconfig['url'],
|
||||
"copyright": inconfig['copyright'],
|
||||
"version": inconfig['version'],
|
||||
"resource_path": reverse_domain(inconfig['url']).replace('.', '/'),
|
||||
|
||||
# Paths..
|
||||
"sourcetree": 'gimp',
|
||||
"outtree": outdir,
|
||||
"icon": inconfig['icon'],
|
||||
"splash": inconfig['splash'],
|
||||
"scalable": inconfig['scalable'],
|
||||
|
||||
# heckimp internal variables
|
||||
"year": str(datetime.datetime.now().year),
|
||||
"us": "heckimp",
|
||||
"usversion": __version__,
|
||||
"uscopy": "Aldercone Studio",
|
||||
"shadow": shadow,
|
||||
}
|
||||
sourceroot = Path(derivedconfig["sourcetree"])
|
||||
|
||||
# precompute string replacements
|
||||
for r in FILENAME_REGEXP:
|
||||
r.replacement = r.replacement.format(**derivedconfig)
|
||||
for r in CONTENTS_REGEXP:
|
||||
r.replacement = r.replacement.format(**derivedconfig)
|
||||
make_directories = [f.format(**derivedconfig) for f in MAKE_DIRECTORIES]
|
||||
|
||||
if pargs.dump:
|
||||
# we can probably output regexes and stuff too but that seems a bit excessive right now.
|
||||
print("Dumping configuration:")
|
||||
print("## Input Configuration")
|
||||
pprint.pprint(inconfig)
|
||||
print("## Derived Configuration")
|
||||
pprint.pprint(derivedconfig)
|
||||
return 0
|
||||
|
||||
# visit each file in the source tree,
|
||||
files = list(sourceroot.rglob('*'))
|
||||
|
||||
if pargs.no_progress:
|
||||
loop = files
|
||||
print("Processing codebase")
|
||||
else:
|
||||
loop = tqdm.tqdm(files, desc="Processing input codebase", unit="file", dynamic_ncols=True, leave=False)
|
||||
|
||||
for d in make_directories:
|
||||
logger.info(f"making path ahead of time: {d}")
|
||||
Path(d).mkdir(exist_ok=True, parents=True)
|
||||
for path in loop:
|
||||
if one_matches(FILENAME_EXCEPTIONS, str(path)):
|
||||
logger.info(f"skipping excluded path {path}")
|
||||
continue
|
||||
# possibly rename file,
|
||||
outpath = Path(apply_regexps(FILENAME_REGEXP, [], str(path)))
|
||||
logger.info(f"processing {outpath}")
|
||||
# write each file to the outtreea
|
||||
if path.is_dir():
|
||||
outpath.mkdir(exist_ok=True, parents=True)
|
||||
logger.info(f"making output directory -> {outpath}")
|
||||
else:
|
||||
#
|
||||
# fixme we should make a more functional way of doing all these special cases, like associating
|
||||
# the special regexs with a symbol that we match on instead so we can treat several of them the same
|
||||
# way
|
||||
#
|
||||
# do things to the contents
|
||||
m = ICON_REGEXP.match(str(path))
|
||||
n = MASCOT_REGEXP.match(str(path))
|
||||
# FIXME we should separate this special processing stuff into some separate functions, especially
|
||||
# the image resizing thing.
|
||||
if SPLASH_REGEXP.match(str(path)):
|
||||
logger.info(f"copying splash screen {derivedconfig['splash']}")
|
||||
# fixme instead of breaking this should copy a default heckimp splash
|
||||
splash = find_file(derivedconfig['splash'], searchpaths)
|
||||
shutil.copy(str(splash), str(outpath))
|
||||
elif SCALABLE_REGEXP.match(str(path)) or MASCOT_SCALABLE_REGEXP.match(str(path)):
|
||||
scalable = find_file(derivedconfig['scalable'], searchpaths)
|
||||
shutil.copy(str(scalable), str(outpath))
|
||||
elif LOGO_REGEXP.match(str(path)):
|
||||
iconpath = find_file(derivedconfig['icon'], searchpaths)
|
||||
icon = Image.open(str(iconpath))
|
||||
icon.thumbnail((256, 256), Image.ANTIALIAS)
|
||||
icon.save(str(outpath), 'PNG')
|
||||
elif m:
|
||||
# size icon for size specified in the path
|
||||
size = int(m.groups()[0])
|
||||
logger.info(f"resizing icon {derivedconfig['icon']} to {size}")
|
||||
iconpath = find_file(derivedconfig['icon'], searchpaths)
|
||||
icon = Image.open(str(iconpath))
|
||||
icon.thumbnail((size, size), Image.ANTIALIAS)
|
||||
icon.save(str(outpath), 'PNG')
|
||||
elif n:
|
||||
# this is identical to the above so we should reuse the code
|
||||
# size icon for size specified in the path
|
||||
size = int(n.groups()[0])
|
||||
logger.info(f"resizing icon {derivedconfig['icon']} to {size}")
|
||||
iconpath = find_file(derivedconfig['icon'], searchpaths)
|
||||
icon = Image.open(str(iconpath))
|
||||
icon.thumbnail((size, size), Image.ANTIALIAS)
|
||||
icon.save(str(outpath), 'PNG')
|
||||
elif one_matches(SKIP_PROCESSING, str(path)) or (path.suffix in PROBABLY_BINARY_EXTENSIONS) or is_binary(path):
|
||||
logger.info(f"skipping processing {outpath}")
|
||||
shutil.copy(str(path), str(outpath))
|
||||
else:
|
||||
# process each line of file to produce output
|
||||
process_text_file(CONTENTS_REGEXP, CONTENTS_EXCEPTIONS, path, outpath)
|
||||
# re-sort library definition files=
|
||||
if re.match(r".*\.def$", str(outpath)):
|
||||
logger.info(f"sorting definition file {outpath}")
|
||||
inf = outpath.read_text().split('\n')
|
||||
outpath.write_text(inf[0]+('\n'.join(sorted(inf[1:], key=lambda d: d.strip()))))
|
||||
|
||||
if not pargs.nogit:
|
||||
os.chdir(derivedconfig['outtree'])
|
||||
os.system('git init .')
|
||||
os.system('git add .')
|
||||
os.system(f"""git commit -m "Initial checkin of {derivedconfig['symname']} from {derivedconfig["us"]}" """)
|
||||
os.system(f"""git tag v{derivedconfig['version']}""")
|
||||
print("Done.")
|
||||
return 0
|
||||
|
||||
|
||||
def main():
|
||||
try:
|
||||
sys.exit(heckimp(sys.argv))
|
||||
except Exception as inst:
|
||||
# fixme log exception
|
||||
print(f"Exception! {inst}")
|
||||
print(traceback.format_exc())
|
||||
sys.exit(-1)
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -0,0 +1,199 @@
|
|||
import re
|
||||
|
||||
from typing import List, Union
|
||||
|
||||
from .regexptools import Replacement
|
||||
R = Replacement
|
||||
|
||||
"""
|
||||
This module contains all of the specially crafted regexps for the GnuImp source code.
|
||||
"""
|
||||
|
||||
#
|
||||
# These regexps are applied to all files, and are the heart of our replacements.
|
||||
#
|
||||
CONTENTS_REGEXP: List[Replacement] = [
|
||||
## Hyperspecific stuff that needs to override later stuff.
|
||||
# Copyright notice update with maximal adherence.
|
||||
R(
|
||||
re.compile(r"/\* GIMP - The GNU Image Manipulation Program(.*)"),
|
||||
(r"/* {longname} - {expandedname}\n"
|
||||
r" * a rebranding of The GNU Image Manipulation Program (created with {us})\n"
|
||||
r" * A derived work which may be trivial. However, any changes may be (C){year} by {copyright}\n *\n"
|
||||
r" * Original copyright, applying to most contents (license remains unchanged): ")
|
||||
),
|
||||
|
||||
R(
|
||||
re.compile(r'(.*)Spencer Kimball, Peter Mattis and the GIMP Development Team(.*)'),
|
||||
r"\1Based on work by Spencer Kimball, Peter Mattis and the GnuImp Development Team\2"
|
||||
),
|
||||
|
||||
# Stuff that just doesn't munge right
|
||||
R(
|
||||
re.compile(r"#define GIMP\(obj\) \(G_TYPE_CHECK_INSTANCE_CAST \(\(obj\), GIMP_TYPE_GIMP, Gimp\)\)"),
|
||||
r"#define {ufilename}(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), {ufilename}_TYPE_{ufilename}, {symname}))"
|
||||
),
|
||||
R(re.compile(r"typedef struct _Gimp Gimp;"),
|
||||
r"typedef struct _{symname} {symname};"),
|
||||
|
||||
R(
|
||||
re.compile(r'(.*)G_DEFINE_TYPE \(Gimp, gimp, GIMP_TYPE_OBJECT\)(.*)'),
|
||||
r'\1G_DEFINE_TYPE ({symname}, {filename}, {ufilename}_TYPE_OBJECT)\2'
|
||||
),
|
||||
R(re.compile(r"(.*[\t ])_Gimp(.*)"), r"\1_{symname}\2"),
|
||||
R(re.compile(r"(.*)mapGimp(.*)"), r'\1map{symname}\2'),
|
||||
|
||||
## general backdomain for eg. app names and such
|
||||
R(re.compile(r"(.*)org.gimp(.*)"), r'\1{backdomain}\2'),
|
||||
|
||||
|
||||
## canvas shadow crappe (we should constrain this but it's really specific)
|
||||
# this one just whipes out all of the embdedded SVGs
|
||||
R(re.compile(r"""^[ \t]*"M [0-9.]+.* z( )*";"""), ""),
|
||||
# this one puts an empty path for eyes_path
|
||||
R(re.compile(r"static const gchar eyes_path\[\] =.*"), r'static const gchar eyes_path[] = "M z"; // #heckimp skip'),
|
||||
R(re.compile(r"static const gchar wilber_path\[\] =.*"), r'static const gchar mascot_path[] = "{shadow}";'),
|
||||
|
||||
|
||||
## Resource paths
|
||||
R(re.compile(r"(.*)/org/gimp/(.*)"), r'\1{resource_path}\2'),
|
||||
|
||||
## URLs and things # FIXME we need to respect *some* of the original URLs, we'll take the time to track down those
|
||||
## and make exceptions later but for now we'll do a big replace. Stuff is mostly in the dialogs, just grep for
|
||||
## the .org domain in there and we can do a great deal to fix it.
|
||||
# we also need to make sure we're directing people to the original docs, but also making sure they don't bother
|
||||
# their bug tracker, and also allow the user to replace the various kinds of urls (docs, subpaths like bugs, etc)
|
||||
R(re.compile(r"(.*)https?://www.gimp.org(.*)"), r'\1{baseurl}\2'),
|
||||
R(re.compile(r"(.*[ \t\"])(www.)?gimp.org(.*)"), r'\1{baseurl}\3'),
|
||||
|
||||
## general replacements
|
||||
R(re.compile(r"(.*)gimp(.*)"), r'\1{filename}\2'),
|
||||
R(re.compile(r"(.*)GIMP(.*)"), r'\1{ufilename}\2'),
|
||||
R(re.compile(r"(.*)The GNU Image Manipulation Program(.*)"), r"\1{expandedname}\2"),
|
||||
R(re.compile(r"(.*)GNU Image Manipulation Program(.*)"), r"\1{expandedname}\2"),
|
||||
|
||||
## symbol names
|
||||
# various symbols and other things (fixme why don't these match ...something=somethingGimp...)
|
||||
R(re.compile(r"(.*[ \t([{][/_%#*&$@:=+-]?)Gimp([:A-Za-z0-9._ ].*)"), r'\1{symname}\2'),
|
||||
R(re.compile(r"^Gimp([A-Za-z0-9._ -].*)"), r'{symname}\1'),
|
||||
R(re.compile(r"(.*[ \t([{][/_%#*&$@:=+-]?[A-Za-z][A-Za-z0-9]+)Gimp([:A-Za-z0-9._ [-].*)"), r'\1{symname}\2'),
|
||||
R(re.compile(r"(.*=[ ]*[A-Za-z]*)Gimp(.*)"), r'\1{symname}\2'),
|
||||
# some meson stuff
|
||||
R(re.compile(r"(.*)LibGimp(.*)"), r'\1Lib{symname}\2'),
|
||||
# documentation?
|
||||
R(re.compile(r"(.*[#_/.])Gimp(.*)"), r'\1{symname}\2'),
|
||||
R(re.compile(r"(.*)Gimp-tips(.*)"), r'\1{symname}-tips\2'),
|
||||
# Strings and strinng like symbols
|
||||
R(re.compile(r"""(.*['"`][A-Za-z0-9]*)Gimp([A-Za-z0-9.-]*.*)"""), r'\1{symname}\2'),
|
||||
R(re.compile(r"""(.*)Gimp([A-Za-z0-9.-]*['"`].*)"""), r'\1{symname}\2'),
|
||||
|
||||
# special symbol converts (kind of hyperspecific, but we can worry about it later)
|
||||
R(re.compile(r"(.*)import Gimp(.*)"), r'\1import {symname}\2'),
|
||||
R(re.compile(r"(.*)gi.repository.Gimp(.*)"), r'\1gi.repository.{symname}\2'),
|
||||
R(re.compile(r"(.*)Gimp-Python(.*)"), r'\1{symname}-Python\2'),
|
||||
R(re.compile(r"(.*)Gimp-Print(.*)"), r'\1{symname}-Print\2'),
|
||||
R(re.compile(r"(.*)Gimp@Type(.*)"), r'\1{symname}@Type\2'),
|
||||
|
||||
|
||||
# Stuff in the pdb perl scripts (the above symbol convert should work but w/e)
|
||||
R(re.compile(r"(.*s/)Gimp(/.*)"), r'\1{symname}\2'),
|
||||
R(re.compile(r"(.*[$%&])Gimp(::.*)"), r'\1{symname}\2'),
|
||||
|
||||
# Mascot stuff
|
||||
R(re.compile(r"(.*[_-])wilber(.*)"), r"\1mascot\2"),
|
||||
R(re.compile(r"(.*)wilber([_-].*)"), r"\1mascot\2"),
|
||||
R(re.compile(r"(.*[_-])WILBER(.*)"), r"\1MASCOT\2"),
|
||||
|
||||
# Chop out the wilber brush
|
||||
R(re.compile(r"^[ \t]*'Wilber.gih',[ \t]*$"), ''),
|
||||
]
|
||||
|
||||
#
|
||||
# These regexps cause the replacement engine to skip the line once they match. This allows us to selectively skip some
|
||||
# lines in the source code, and also cause a line to become skipped after some replacements.
|
||||
#
|
||||
CONTENTS_EXCEPTIONS: List[re.Pattern] = [
|
||||
# skips generated by heckimp
|
||||
re.compile(r".*#heckimp skip.*"),
|
||||
# email addresses should not be replaced.
|
||||
re.compile(r"(.*)@gimp\.org(.*)"),
|
||||
# don't replace the expansion of GnuImp in the updated copyright notice
|
||||
re.compile(r".*a rebranding of The GNU Image.*"),
|
||||
# we want to preserve our copyright notice without it getting munged (its subtly different from the original)
|
||||
re.compile(r".*Spencer Kimball, Peter Mattis and the GIMP Development Team, .*"),
|
||||
|
||||
# keep our xcfs compatible
|
||||
re.compile(r'.*"gimp xcf.*'),
|
||||
|
||||
# Lets leave all of the weird urls alone for now until we let the user override them explicitly (or we rewrite
|
||||
# the places they show up with our own versions of the strings) # FIXME the way it does skips breaks some
|
||||
# replacements so we should fix this elsewise somehow.
|
||||
#re.compile(r".*docs.gimp.org.*"),
|
||||
#re.compile(r".*developer.gimp.org.*"),
|
||||
#re.compile(r".wiki.gimp.org.*"),
|
||||
re.compile(r".*irc.*gimp.org:6667.*"),
|
||||
re.compile(r".*meetthegimp.org.*"), # wtf this domain
|
||||
]
|
||||
|
||||
#
|
||||
# This is the core filename renaming regexps.
|
||||
#
|
||||
FILENAME_REGEXP: List[Replacement] = [
|
||||
# We'll stick all this historical cruft in its own directory.
|
||||
R(re.compile(r"^gimp/README"), r'{filename}/upstream-documentation/README.upstream'),
|
||||
R(re.compile(r"^gimp/(README.*)"), r'{filename}/upstream-documentation/\1'),
|
||||
R(re.compile(r"^gimp/(Change[Ll]og.*)"), r'{filename}/upstream-documentation/\1'),
|
||||
R(re.compile(r"^gimp/(NEWS.*)"), r'{filename}/upstream-documentation/\1'),
|
||||
R(re.compile(r"^gimp/MAINTAINERS"), r'{filename}/upstream-documentation/MAINTAINERS.upstream'),
|
||||
|
||||
# other filename replacements
|
||||
R(re.compile(r"(.*)org.gimp(.*)"), r'\1{backdomain}\2'),
|
||||
R(re.compile(r"(.*)gimp(.*)"), r'\1{filename}\2'),
|
||||
R(re.compile(r"(.*)GIMP(.*)"), r'\1{ufilename}\2'),
|
||||
R(re.compile(r"(.*-)wilber(.*)"), r'\1mascot\2'),
|
||||
]
|
||||
|
||||
#
|
||||
# If a file matches one of these, it is skipped entirely (not copied or processed)
|
||||
#
|
||||
FILENAME_EXCEPTIONS: List[re.Pattern] = [
|
||||
# We just leave the git out of it.
|
||||
# If the fork should have a git repository it should probably not be connected to orignal.
|
||||
re.compile(r"^gimp/\.git/.*"),
|
||||
# re.compile(r"^gimp.icons.Legacy.*") # it'd be cool to chop this out but we'd have to patch icons/meson.build
|
||||
|
||||
# just chop out all the random Wilber images (including the brush)
|
||||
re.compile(r".*Wilber*"),
|
||||
]
|
||||
|
||||
#
|
||||
# If a file matches one of these we skip processing but do copy it. We may have separate processes that also deal with
|
||||
# these. FIXME we should proxbably process LICENSE, and copy NEWS and CHANGELOG etc to a subdirectory so they don't
|
||||
# confuse the fork.
|
||||
#
|
||||
SKIP_PROCESSING: List[re.Pattern] = [
|
||||
re.compile(r"^gimp/LICENSE"),
|
||||
re.compile(r"^gimp/Change[Ll]og.*"),
|
||||
re.compile(r"^gimp/NEWS.*"),
|
||||
re.compile(r"^gimp/authors.*"),
|
||||
re.compile(r"^gimp/AUTHORS"),
|
||||
re.compile(r"^gimp/MAINTAINERS"),
|
||||
]
|
||||
|
||||
#
|
||||
# regexp that print extra debug info when a line matches them
|
||||
#
|
||||
DEBUG_REGEXP: List[re.Pattern] = [
|
||||
# re.compile(r"^[ \t]+static const GimpEnum.*"),
|
||||
]
|
||||
|
||||
# Regexes to match various specific files for special processing
|
||||
SPLASH_REGEXP = re.compile(r".*gimp-splash.png")
|
||||
ICON_REGEXP = re.compile(r".*desktop.([0-9]{2,3})x[0-9]{2,3}.gimp.png")
|
||||
SCALABLE_REGEXP = re.compile(r".*desktop.scalable.gimp.svg")
|
||||
LOGO_REGEXP = re.compile(r".*data.images.gimp-(devel-)?logo.png")
|
||||
MASCOT_SCALABLE_REGEXP = re.compile(r"icons.(Symbolic|Color).scalable.gimp-wilber.*")
|
||||
MASCOT_REGEXP = re.compile(r"icons.(Symbolic|Color|Lecgacy).([0-9]{2,3}).gimp-wilber.*")
|
||||
|
||||
|
||||
MAKE_DIRECTORIES = ['{filename}/upstream-documentation']
|
|
@ -0,0 +1,70 @@
|
|||
import logging
|
||||
import urllib.parse
|
||||
|
||||
import tqdm # type: ignore
|
||||
|
||||
from typing import List, Set
|
||||
from pathlib import Path
|
||||
|
||||
"""
|
||||
Miscellaneous utilities and constants.
|
||||
"""
|
||||
|
||||
|
||||
PROBABLY_BINARY_EXTENSIONS: List[str] = [
|
||||
".ico",
|
||||
".ppm",
|
||||
".pnm",
|
||||
".png",
|
||||
".gz",
|
||||
".svg",
|
||||
".pgm",
|
||||
".gih",
|
||||
".gbr",
|
||||
]
|
||||
|
||||
|
||||
class TqdmLoggingHandler(logging.Handler):
|
||||
"""
|
||||
A simple logging wrapper that won't clobber TQDM's progress bar.
|
||||
"""
|
||||
def __init__(self, level=logging.NOTSET):
|
||||
super().__init__(level)
|
||||
|
||||
def emit(self, record):
|
||||
try:
|
||||
msg = self.format(record)
|
||||
tqdm.tqdm.write(msg)
|
||||
self.flush()
|
||||
except Exception:
|
||||
self.handleError(record)
|
||||
|
||||
|
||||
## Utility Functions
|
||||
def is_binary(path: Path, amount: int = 256) -> bool:
|
||||
"""
|
||||
A pretty sloppy way to determine if a file is binary or not. Takes a path and returns true/false. Bigger
|
||||
amount means more accurate (more of the file read).
|
||||
"""
|
||||
try:
|
||||
b = path.open().read(amount)
|
||||
return False
|
||||
except UnicodeDecodeError:
|
||||
return True
|
||||
|
||||
def reverse_domain(url: str) -> str:
|
||||
"""
|
||||
Create a 'reverse domain' used for labeling objects and whatnot.
|
||||
"""
|
||||
p = urllib.parse.urlparse(url)
|
||||
domain = p.netloc
|
||||
return '.'.join(reversed(domain.split('.')))
|
||||
|
||||
def find_file(fname: str, searchpaths: Set[Path]) -> Path:
|
||||
"""
|
||||
Return a path object for the first file we find in the search paths with the filename or raise FileNotFound error.
|
||||
"""
|
||||
for path in searchpaths:
|
||||
if (path / fname).exists():
|
||||
return (path / fname)
|
||||
raise FileNotFoundError(f"can't find {fname} in searchpaths")
|
|
@ -0,0 +1,80 @@
|
|||
"""
|
||||
Various functios to process files and work with regexps and Replacement objects
|
||||
"""
|
||||
|
||||
import re
|
||||
import logging
|
||||
|
||||
from collections.abc import Iterable
|
||||
from pathlib import Path
|
||||
from typing import cast, Sequence, Union, Optional
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# This is a generic type that's a regexp with its replacement (it needs to be mutatable but we should
|
||||
# add a lot of the below functionality to the class itself, for example we could have __call__ call the
|
||||
# regex.match, we could make a function that configures the replacement based on a dictionary, we could
|
||||
# make a function that executes the replacement on a string, etc. It would probably make the below code
|
||||
# a bit less type dependent).
|
||||
class Replacement:
|
||||
def __init__(self, regexp, replacement):
|
||||
self.regexp = regexp
|
||||
self.replacement = replacement
|
||||
|
||||
|
||||
def one_matches(relist: Sequence[Union[re.Pattern, Replacement, Sequence]], s: str) -> Union[None, re.Pattern, Replacement, Sequence]:
|
||||
"""
|
||||
Return True if one of the regular expressions, patterns, or 0th elements in Sequence of lists matches.
|
||||
"""
|
||||
for reg in relist:
|
||||
if isinstance(reg, Replacement):
|
||||
r = reg.regexp
|
||||
elif isinstance(reg, re.Pattern):
|
||||
r = reg
|
||||
elif isinstance(reg, Iterable):
|
||||
r = reg[0]
|
||||
else:
|
||||
raise Exception("got a weird thing")
|
||||
if r.match(s):
|
||||
return reg
|
||||
return None
|
||||
|
||||
def apply_regexps(relist: Sequence[Replacement], exceptlist: Optional[Sequence[re.Pattern]]=None, s: str = "", debuglist: Optional[Sequence[re.Pattern]]=None) -> str:
|
||||
"""
|
||||
Return the string after applying each Replacement in the list passed as relist.
|
||||
"""
|
||||
if debuglist is None:
|
||||
debuglist = []
|
||||
if exceptlist is None:
|
||||
exceptlist = []
|
||||
res = s
|
||||
debug = False
|
||||
if one_matches(debuglist, s):
|
||||
debug = True
|
||||
logger.debug(f"-- regexp start {s}")
|
||||
|
||||
while True:
|
||||
m: Replacement = cast(Replacement, one_matches(relist, res))
|
||||
if debug and m is not None:
|
||||
logger.debug(m)
|
||||
if m is None:
|
||||
break;
|
||||
if one_matches(exceptlist, res):
|
||||
if debug:
|
||||
logger.debug("exceptlist: "+str(one_matches(exceptlist, res)))
|
||||
break
|
||||
|
||||
res = m.regexp.sub(m.replacement, res)
|
||||
if debug:
|
||||
logger.debug(f"-- regexp done {res}")
|
||||
return res
|
||||
|
||||
|
||||
def process_text_file(relist: Sequence[Replacement], exceptlist: Sequence[re.Pattern], infile: Path, outfile: Path, debug_regexps: Sequence[re.Pattern] = None):
|
||||
if debug_regexps is None:
|
||||
debug_regexps = []
|
||||
with infile.open("r") as inf, outfile.open("w") as outf:
|
||||
fixedlines = [apply_regexps(relist, exceptlist, line, debug_regexps) for line in inf.readlines()]
|
||||
outf.writelines(fixedlines)
|
|
@ -0,0 +1,40 @@
|
|||
[metadata]
|
||||
name = heckimp
|
||||
version = 0.1.1
|
||||
description = Rename and rebrand Gnu Image Manipulation Program
|
||||
author = Aldercone Studio
|
||||
author_email = alderconestudio@gmail.com
|
||||
keywords = text, graphics, rebranding, code processing
|
||||
long_description = file: README.md
|
||||
long_description_content_type = text/markdown
|
||||
license_file = LICENSE
|
||||
url=https://heckin.technology/AlderconeStudio/heckimp
|
||||
license = No Nazis (otherwise BSD 1 clause)
|
||||
platform = any
|
||||
classifiers =
|
||||
Development Status :: 3 - Alpha
|
||||
Intended Audience :: Developers
|
||||
License :: OSI Approved :: BSD License
|
||||
Operating System :: OS Independent
|
||||
Programming Language :: Python
|
||||
Programming Language :: Python :: 3.7
|
||||
Programming Language :: Python :: 3.8
|
||||
Programming Language :: Python :: 3.9
|
||||
Programming Language :: Python :: 3.10
|
||||
Programming Language :: Python :: 3.11
|
||||
Topic :: Artistic Software
|
||||
Topic :: Text Processing
|
||||
|
||||
[options]
|
||||
packages =
|
||||
heckimp
|
||||
|
||||
zip_safe = true
|
||||
install_requires =
|
||||
tqdm
|
||||
pillow
|
||||
|
||||
|
||||
[options.entry_points]
|
||||
console_scripts =
|
||||
heckimp = heckimp.__main__:main
|
Loading…
Reference in New Issue