Created
May 29, 2018 06:17
-
-
Save mjtorn/1d1486eaf611dea465ff5563bf3fe3e2 to your computer and use it in GitHub Desktop.
To export builds/releases of an Escoria game
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
""" | |
Assumes you have three templates: "Linux/X11", "Mac OSX" and "Windows Desktop" | |
all propertly configured with export templates and settings. | |
Version information should be %VERSION% throughout. | |
This script will replace that and some project settings for the build and then | |
reset them back. | |
USAGE: ./export.py GODOT_EXECUTABLE OUTPUT_DIRECTORY | |
""" | |
import os | |
import shutil | |
import subprocess | |
import sys | |
import zipfile | |
def get_head(*, cwd='.'): | |
cmd = 'git show HEAD --abbrev-commit'.split() | |
escoria_head_lines = subprocess.check_output(cmd, cwd=cwd).split(b'\n') | |
escoria_head_line = escoria_head_lines[0] | |
assert(escoria_head_line.startswith(b'commit')) | |
return escoria_head_line.split()[1].decode('utf-8') | |
def get_tag(*, cwd='.'): | |
cmd = 'git ls-remote -t origin'.split() | |
escoria_tags = subprocess.check_output(cmd, cwd=cwd).split(b'\n') | |
escoria_tags = [t for t in escoria_tags if t] | |
escoria_tag_line = escoria_tags[-1] | |
assert(escoria_tag_line.endswith(b'^{}')) | |
escoria_tag_line = escoria_tag_line[:-3] | |
escoria_tag_line = escoria_tag_line.replace(b'refs/tags/', b'') | |
escoria_commit, escoria_tag = escoria_tag_line.split(b'\t') | |
escoria_commit = escoria_commit[:7].decode('utf-8') | |
escoria_tag = escoria_tag.decode('utf-8') | |
return escoria_commit, escoria_tag | |
def get_escoria_head(): | |
return get_head(cwd='..') | |
def get_escoria_tag(): | |
return get_tag(cwd='..') | |
def get_game_version(): | |
escoria_head = get_escoria_head() | |
escoria_commit, escoria_tag = get_escoria_tag() | |
game_head = get_head() | |
game_commit, game_tag = get_tag() | |
# Enforce that main Escoria is tagged to the same release as the game data | |
assert escoria_tag == game_tag, '{} != {}'.format(escoria_tag, game_tag) | |
if escoria_commit != escoria_head: | |
escoria_version = '{}-{}'.format(escoria_tag, escoria_head) | |
else: | |
escoria_version = escoria_tag | |
if game_commit != game_head: | |
game_version = '{}-{}'.format(escoria_version, game_head) | |
else: | |
game_version = escoria_version | |
return game_version | |
def set_game_version(game_version, *, unset): | |
fname = '../export_presets.cfg' | |
with open(fname, 'rb') as f: | |
export_presets = f.read() | |
if not unset: | |
export_presets.replace(b'%VERSION%', game_version.encode('utf-8')) | |
else: | |
export_presets.replace(game_version.encode('utf-8'), b'%VERSION%') | |
with open(fname, 'wb') as f: | |
f.write(export_presets) | |
def set_export_settings(*, unset): | |
fname = '../project.godot' | |
replaces = ( | |
(b'run/disable_stdout=false', b'run/disable_stdout=true'), | |
(b'run/disable_stderr=false', b'run/disable_stderr=true'), | |
(b'window/size/resizable=false', b'window/size/resizable=true'), | |
(b'window/size/fullscreen=false', b'window/size/fullscreen=true'), | |
) | |
with open(fname, 'rb') as f: | |
project_godot = f.read() | |
if not unset: | |
i = 0 | |
j = 1 | |
else: | |
i = 1 | |
j = 0 | |
for replacement in replaces: | |
src = replacement[i] | |
dst = replacement[j] | |
project_godot.replace(src, dst) | |
with open(fname, 'wb') as f: | |
f.write(project_godot) | |
def finalize_linux(dest_dir): | |
"""Work around how Godot doesn't know its own files | |
""" | |
src = os.path.join(dest_dir, '..', '..', 'build.isala.001.linux', 'ironsky-alunaradventure/') | |
dst = os.path.join(dest_dir, 'ironsky-alunaradventure') | |
shutil.copytree(src, dst) | |
return 0 | |
def finalize_osx(dest_dir): | |
"""Work around how Godot doesn't know its own files, or chmod +x for osx | |
""" | |
# Have to unzip to chmod | |
zip_path = os.path.join(dest_dir, 'is-ala.zip') | |
with zipfile.ZipFile(zip_path) as zf: | |
zf.extractall(path=dest_dir) | |
contents_dir = os.path.join(dest_dir, 'Iron Sky: A Lunar Adventure.app', 'Contents') | |
resources_dir = os.path.join(dest_dir, 'Iron Sky: A Lunar Adventure.app', 'Contents', 'Resources') | |
executable = os.path.join(contents_dir, 'MacOS', 'Iron Sky: A Lunar Adventure') | |
os.chmod(executable, 0o755) | |
src = os.path.join(dest_dir, '..', '..', 'build.isala.001.linux', 'ironsky-alunaradventure/') | |
dst = os.path.join(resources_dir, 'ironsky-alunaradventure') | |
shutil.copytree(src, dst) | |
os.unlink(zip_path) # Because otherwise it would be included in the release zip | |
return 0 | |
def finalize_win(dest_dir): | |
"""Work around how Godot doesn't know its own files | |
""" | |
src = os.path.join(dest_dir, '..', '..', 'build.isala.001.linux', 'ironsky-alunaradventure/') | |
dst = os.path.join(dest_dir, 'ironsky-alunaradventure') | |
shutil.copytree(src, dst) | |
return 0 | |
def export_game(godot, dest_root, game_version, *, extrafunc=None, abbrev=None, template=None): | |
# Setup phase | |
dirname = 'isala-{}-{}'.format(game_version, abbrev) | |
dest_dir = os.path.join(dest_root, dirname) | |
if abbrev == 'x11': | |
dest_file = os.path.join(dest_dir, 'is-ala.x86_64') | |
elif abbrev == 'osx': | |
dest_file = os.path.join(dest_dir, 'is-ala.zip') | |
elif abbrev == 'win': | |
dest_file = os.path.join(dest_dir, 'is-ala.exe') | |
else: | |
raise RuntimeError('Unsupported platform {}'.format(abbrev)) | |
zf_name = '{}.zip'.format(dest_dir) | |
zf_path = os.path.join(dest_root, zf_name) | |
# Cleanup phase | |
if os.path.exists(dest_dir): | |
shutil.rmtree(dest_dir) | |
if os.path.exists(zf_path): | |
os.unlink(zf_path) | |
os.mkdir(dest_dir) | |
# Execution phase | |
cmd = [godot, '--export', template, dest_file] | |
retval = subprocess.call(cmd, cwd='..') | |
assert retval == 0, retval | |
# Finalization phase | |
if extrafunc: | |
retval = extrafunc(dest_dir) | |
assert retval == 0, retval | |
# Packaging phase | |
with zipfile.ZipFile(zf_path, mode='w', compression=zipfile.ZIP_DEFLATED) as zf: | |
for dirpath, dirnames, filenames in os.walk(dest_dir): | |
orig_dirpath = dirpath | |
dirpath = dirpath.replace(dest_dir, os.path.basename(dest_dir)) | |
for fname in filenames: | |
arcname = os.path.join(dirpath, fname) | |
zf.write(os.path.join(orig_dirpath, fname), arcname=arcname) | |
for dname in dirnames: | |
arcname = os.path.join(dirpath, dname) | |
zf.write(os.path.join(orig_dirpath, dname), arcname=arcname) | |
def export_linux(godot, dest_root, game_version): | |
export_game(godot, dest_root, game_version, extrafunc=finalize_linux, abbrev='x11', template='Linux/X11') | |
def export_osx(godot, dest_root, game_version): | |
export_game(godot, dest_root, game_version, extrafunc=finalize_osx, abbrev='osx', template='Mac OSX') | |
def export_win(godot, dest_root, game_version): | |
export_game(godot, dest_root, game_version, extrafunc=finalize_win, abbrev='win', template='Windows Desktop') | |
def main(godot, dest_root): | |
game_version = get_game_version() | |
set_game_version(game_version, unset=False) | |
set_export_settings(unset=False) | |
export_linux(godot, dest_root, game_version) | |
export_osx(godot, dest_root, game_version) | |
export_win(godot, dest_root, game_version) | |
set_export_settings(unset=True) | |
set_game_version(game_version, unset=True) | |
return 0 | |
if __name__ == '__main__': | |
script = os.path.basename(sys.argv[0]) | |
args = sys.argv[1:] | |
if len(args) != 2: | |
print('USAGE: ./{} GODOT_EXECUTABLE OUTPUT_DIRECTORY'.format(script)) | |
sys.exit(1) | |
if not os.path.exists(script): | |
print('Run in the script directory') | |
sys.exit(main(*args)) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment