SE2Revival/tools/package_toolkit.py

95 lines
4.9 KiB
Python

"""Build the SE2 Revival package: dist/SE2-Revival/ and dist/SE2-Revival.zip.
usage: python -I tools/package_toolkit.py [--version TAG] (after python -I mods/build.py)
Contents (extract next to SiegeEditor2.exe of GPG's Dungeon Siege II Tool Kit 1.1, which is not included):
version.dll Ultimate ASI Loader x86 (build/asiloader/dinput8.dll, unmodified, renamed)
scripts/DS2Toolkit.asi the tool kit plugin (mods/toolkit)
scripts/DS2Toolkit.ini its settings (mods/toolkit/DS2Toolkit.ini)
bw/BWCompat.ds2dll Broken World script functions for the editor (mods/bwcompat, renamed exports)
README.txt the install guide as plain text (INSTALL.md in the public repository, exported from
tools/se2_install.md); --version TAG names the release in it
THIRD-PARTY-NOTICES.txt
Runs on Windows and on Linux (CI: tools/ci_build.py --project se2).
"""
import os, re, shutil, subprocess, sys, textwrap, zipfile
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
MODS = os.path.join(ROOT, 'build', 'mods')
DIST = os.path.join(ROOT, 'dist', 'SE2-Revival')
LOADER = os.path.join(ROOT, 'build', 'asiloader', 'dinput8.dll')
GUIDES = [os.path.join(ROOT, 'INSTALL.md'), os.path.join(ROOT, 'tools', 'se2_install.md')] # public repo, here
def plain_text(md, version=None):
"""The guide's Markdown as plain text: headings underlined, tables as lists, no backticks or bold."""
out, rows, header, indent = [], [], [], ['']
def flush():
for cells in rows:
mid = ''.join(' (%s: %s)' % (header[i].lower(), cells[i]) for i in range(1, len(cells) - 1))
text = '- %s%s: %s' % (cells[0], mid, cells[-1]) if len(cells) > 1 else '- ' + cells[0]
out.extend(textwrap.wrap(text, 116, initial_indent=indent[0], subsequent_indent=indent[0] + ' ',
break_long_words=False, break_on_hyphens=False))
rows.clear()
header.clear()
for line in md.splitlines():
line = line.replace('`', '').replace('**', '').rstrip()
if line.lstrip().startswith('|'):
if set(line.replace('|', '').strip()) <= set('-: '):
continue # the |---| line under the header
cells = [c.strip() for c in line.strip().strip('|').split('|')]
if header:
rows.append(cells)
else:
header.extend(cells)
indent[0] = line[:len(line) - len(line.lstrip())]
continue
flush()
m = re.match(r'(#+) (.*)', line)
if m:
out += [m.group(2), ('=' if len(m.group(1)) == 1 else '-') * len(m.group(2))]
if len(m.group(1)) == 1 and version:
out += ['', 'Version ' + version]
continue
out.append(line)
flush()
return '\n'.join(out).strip() + '\n'
def main():
a = sys.argv[1:]
version = a[a.index('--version') + 1] if '--version' in a else None
for f in ('DS2Toolkit.asi', 'BWCompat.asi'):
if not os.path.exists(os.path.join(MODS, f)):
sys.exit('build/mods/%s missing - run python -I mods/build.py first' % f)
if not os.path.exists(LOADER):
sys.exit('%s missing - Ultimate ASI Loader x86 dinput8.dll (tools/ci_build.py fetches it)' % LOADER)
if os.path.isdir(DIST):
shutil.rmtree(DIST)
os.makedirs(os.path.join(DIST, 'scripts'))
os.makedirs(os.path.join(DIST, 'bw'))
shutil.copy(LOADER, os.path.join(DIST, 'version.dll'))
shutil.copy(os.path.join(MODS, 'DS2Toolkit.asi'), os.path.join(DIST, 'scripts'))
subprocess.run([sys.executable, '-I', os.path.join(ROOT, 'tools', 'pe_rename_exports.py'),
os.path.join(MODS, 'BWCompat.asi'), os.path.join(DIST, 'bw', 'BWCompat.ds2dll'),
os.path.join(ROOT, 'mods', 'bwcompat', 'bwcompat.map')], check=True)
guide = next(g for g in GUIDES if os.path.exists(g))
with open(os.path.join(DIST, 'README.txt'), 'w', encoding='utf-8', newline='\r\n') as f:
f.write(plain_text(open(guide, encoding='utf-8').read(), version))
for name in ('scripts/DS2Toolkit.ini', 'THIRD-PARTY-NOTICES.txt'): # CRLF for Notepad, whatever the checkout has
src = os.path.join(ROOT, 'mods', 'toolkit', 'DS2Toolkit.ini') if name.startswith('scripts') else \
os.path.join(ROOT, 'tools', 'third_party', 'THIRD-PARTY-NOTICES.txt')
data = open(src, 'rb').read()
with open(os.path.join(DIST, name), 'wb') as f:
f.write(data.replace(b'\r\n', b'\n').replace(b'\n', b'\r\n'))
z = DIST + '.zip'
with zipfile.ZipFile(z, 'w', zipfile.ZIP_DEFLATED) as zf:
for d, _, names in sorted(os.walk(DIST)):
for n in sorted(names):
p = os.path.join(d, n)
zf.write(p, os.path.relpath(p, DIST))
print('wrote', z, os.path.getsize(z), 'bytes')
if __name__ == '__main__':
main()