Skip to content

Instantly share code, notes, and snippets.

@mjtorn
Created May 29, 2018 06:17
Show Gist options
  • Save mjtorn/1d1486eaf611dea465ff5563bf3fe3e2 to your computer and use it in GitHub Desktop.
Save mjtorn/1d1486eaf611dea465ff5563bf3fe3e2 to your computer and use it in GitHub Desktop.
To export builds/releases of an Escoria game
#!/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