Skip to content

Commit

Permalink
added compiled build
Browse files Browse the repository at this point in the history
  • Loading branch information
Robert-N7 committed Jun 27, 2021
1 parent 5471b76 commit 8a67b4f
Show file tree
Hide file tree
Showing 18 changed files with 524 additions and 14 deletions.
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,9 @@
Temp*
tmp*
venv*

akmpt/build/main*
akmpt/dist/main*
akmpt/dist/akmpt*
akmpt/build/version
akmpt/main.spec
6 changes: 2 additions & 4 deletions .idea/runConfigurations/reverse.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ It also rotates the starting point around the start line and respawns around the
*It is only meant as a starting place for reversal, and further manual adjustments will be required.*

# Install
The KMP tool can be installed as a python package.
Download compiled [releases](https://github.com/Robert-N7/akmpt/releases) from the release page.

The KMP tool can also be installed as a python package.
```
pip install git+https://github.com/Robert-N7/akmpt.git
```
Expand Down
2 changes: 1 addition & 1 deletion akmpt/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ def run(self):
class RotateRunner(GenericRunner):
cmd = 'rotate'
named_args = ('destination', 'group', 'item', 'rotation', 'direction')
help_string = 'rotates the kmp <group> [<item>] by rotation (default 180)'
help_string = 'Rotates the kmp <group> [<item>] by rotation (default 180)'
rotation_default = 180
group_default = 'game_objects'
direction_default = 'y'
Expand Down
214 changes: 214 additions & 0 deletions akmpt/build/build.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
#!/usr/bin/env python3
import os
import platform
import shutil
import subprocess
import sys
import time

from check_imports import ImportChecker
from get_bit_width import get_bit_width
from update_version import run_update_version


def which(program):
def is_exe(exe_file):
return os.path.isfile(exe_file) and os.access(exe_file, os.X_OK)

for path in os.environ["PATH"].split(os.pathsep):
exe_file = os.path.join(path, program)
if is_exe(exe_file):
return exe_file
exe_file += '.exe'
if is_exe(exe_file):
return exe_file


def run_sub_process(args, cwd, program=None):
if program is None:
li = [sys.executable]
else:
li = [program]
li.extend(args)
return subprocess.Popen(li, cwd=cwd)


def build_distribution(version):
interpreter = sys.executable
os.chdir('../dist')
# read configuration
bit_width = get_bit_width(interpreter)
name = 'main'
build_type = 'dir'
# build
clean([name, ], [name + '.exe', name, ])
my_platform = platform.system().lower()
if 'windows' in my_platform:
makensis = which('makensis')
if not makensis:
raise FileNotFoundError('makensis')
win_zip = which('7z')
if not win_zip:
raise FileNotFoundError('7z')
else:
makensis = win_zip = None
out_file, is_dir = build(name, build_type, interpreter, my_platform)
if not out_file:
sys.exit(1)
dist_dir = 'akmpt_' + my_platform + '-' + platform.release() + '_' \
+ bit_width + '-' + version
# clean
clean([dist_dir], (dist_dir + '.zip', dist_dir + '.tar.gz', 'dist_dir'))
# dist
if not make_distribution(dist_dir, my_platform, out_file, is_dir, makensis, win_zip):
sys.exit(1)


def main(args):
# update version/bit_width
start = time.time()
if (len(args)):
version = args[0]
else:
print('Version required!')
sys.exit(1)
run_update_version([version])
abspath = os.path.abspath(__file__)
x = ImportChecker(os.path.dirname(os.path.dirname(abspath)))
x.check_imports()
build_distribution(version)
print(f'Finished in {round(time.time() - start, 2)} secs.')


def tar(path):
return not os.system('tar czvf ' + path + '.tar.gz ' + path)


def zip(path, win_zip):
return not os.system(f'"{win_zip}" a -tzip {path}.zip {path}')


def make_distribution(dir, platform, binary_path, binary_path_is_dir, make_nsis, win_zip):
os.mkdir(dir)
print(f'Making distribution in {dir}')
shutil.copy('../../LICENSE', dir)
shutil.copy('../../README.md', dir)
# etc = dir + '../etc/akmpt'
# copy_from = '../../etc/akmpt'
# for file in os.listdir(copy_from):
# path = os.path.join(copy_from, file)
# if not os.path.isdir(path):
# shutil.copy(path, etc)
dest_dir = os.path.join(dir, 'bin')
bin_dir, base_name = os.path.split(binary_path)
exe = os.path.join(dest_dir, 'akmpt')
is_exe = False
if 'exe' in base_name:
is_exe = True
base_name, ext = os.path.splitext(base_name)
if binary_path_is_dir:
shutil.copytree(bin_dir, dest_dir)
if is_exe:
shutil.move(os.path.join(dest_dir, base_name + '.exe'), exe + '.exe')
else:
shutil.move(os.path.join(dest_dir, base_name), exe)
else:
os.mkdir(dest_dir)
shutil.copy(binary_path, exe)
# platform specific files
if platform == 'windows':
shutil.copy('./install-win.txt', dir)
shutil.copy('./make_installer.nsi', dir)
os.chdir(dir)
if os.system(f'"{make_nsis}" make_installer.nsi'):
return False
os.remove('./make_installer.nsi')
os.chdir('..')
if not zip(dir, win_zip):
return False
else:
install_file = './install-ubu.txt'
update_os(install_file, platform)
shutil.copy(install_file, os.path.join(dir, 'install.txt'))
if not tar(dir):
os.chdir('..')
return False
os.chdir('..')
return True


def copytree(folder, dest, replace_existing=False):
if not os.path.exists(dest):
os.mkdir(dest)
for file in os.listdir(folder):
path = os.path.join(folder, file)
if os.path.isdir(path):
copytree(path, os.path.join(dest, file), replace_existing)
else:
file_dest = os.path.join(dest, file)
if not os.path.exists(file_dest):
shutil.copy2(path, file_dest)
elif replace_existing:
os.remove(file_dest)
shutil.copy2(path, file_dest)


def update_os(file, platform):
with open(file) as f:
lines = f.readlines()
new_lines = []
for line in lines:
if line.startswith('OS:'):
new_lines.append('OS: ' + platform + '\n')
else:
new_lines.append(line)
with open(file, 'w') as f:
f.write(''.join(new_lines))


def clean(folders, files):
for folder in folders:
if os.path.exists(folder) and os.path.isdir(folder):
shutil.rmtree(folder)
for x in files:
if os.path.exists(x) and os.path.isfile(x):
os.remove(x)


def build(name, build_type, interpreter, platform):
output = None
os.chdir('..')
for x in os.listdir():
if x.endswith('.pyc'):
os.remove(x)
if 'dir' in build_type:
output_type = '--onedir'
elif 'file' in build_type:
output_type = '--onefile'
else:
output_type = '--onedir' if platform == 'windows' else 'onefile'
is_dir = True if 'onedir' in output_type else False
params = '-y __main__.py --name ' + name + ' ' + output_type
print('Current dir is {}'.format(os.getcwd()))
# if platform == 'windows':
params += ' -i ./dist/icon.ico'
result = os.system(interpreter + ' -m PyInstaller ' + params)
os.chdir('dist')
if not result:
output = name if not is_dir else name + '/' + name
if not os.path.exists(output) or os.path.isdir(output):
output += '.exe'
if not os.path.exists(output):
print('Unable to find PyInstaller output file!')
sys.exit(1)
result = os.system(os.path.join(os.getcwd(),
output) + ' reverse ../../tests/fixtures/beginner.kmp '
'../../tests/fixtures/tmp.kmp -o')
if not result:
return output, is_dir
return None, is_dir


if __name__ == '__main__':
main(sys.argv[1:])
print('done')
10 changes: 10 additions & 0 deletions akmpt/build/build_instructions
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
IMPORTANT:
Building the distribution is done through build.py and inputting the version number.
It uses the 'pyinstaller' package to build the binaries. It creates a single file/folder in the 'dist' folder.
The installer for distributions is also created, but may require additional dependencies:

For Windows you need the following on your system path:
1. makensis (windows installer)
a. Additionally the EnVar plug-in is required.
2. 7z (zipping installer)

92 changes: 92 additions & 0 deletions akmpt/build/check_imports.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import os
import re

class ImportChecker:
def __init__(self, root):
self.root = root

def trace_parent(self, module):
parent = self.parents.get(module)
if parent:
return self.trace_parent(parent) + '.' + module
return module

def convert_relative_import(self, line, top_module):
x = self.trace_parent(top_module.strip('.'))
return line.replace(top_module, x, 1)


def check_file(self, file_path, package_modules):
total_warns = 0
with open(file_path) as f:
data = f.readlines()
new_data = []
for i in range(len(data)):
line = data[i]
match = re.search('(from (?P<from>\.?\w+)(\.\w+)* )?import (?P<imported>\w+).*', line)
new_line = None
if match:
d = match.groupdict()
imported = d.get('from')
if not imported:
imported = d.get('imported')
# imported = imported.strip('.')
if imported != 'abmatt':
if imported in package_modules:
print(f'WARN: relative import line {i} in {file_path}: {line}')
new_line = self.convert_relative_import(line, imported)
total_warns += 1
if new_line:
new_data.append(new_line)
else:
new_data.append(line)
if total_warns:
with open(file_path, 'w') as f:
f.write(''.join(new_data))
return total_warns

@staticmethod
def is_package(folder):
return os.path.exists(os.path.join(folder, '__init__.py'))


def gather_modules(self, root_path, parents):
modules = []
module_file_paths = []
parent = os.path.basename(root_path)
for file in os.scandir(root_path):
if file.is_dir():
if self.is_package(file.path):
module = os.path.basename(file.path)
modules.append(module)
if module not in parents:
parents[module] = parent
sub_modules, sub_file_paths = self.gather_modules(file.path, parents)
modules.extend(sub_modules)
module_file_paths.extend(sub_file_paths)
else:
base_name = os.path.basename(file.path)
if base_name.endswith('.py'):
module = base_name[:-3]
modules.append(module)
module_file_paths.append(file.path)
if module not in parents:
parents[module] = parent
return modules, module_file_paths


def check_imports(self):
total_warnings = 0
self.parents = {}
modules, module_paths = self.gather_modules(self.root, self.parents)
for path in module_paths:
total_warnings += self.check_file(path, modules)
if total_warnings:
print(f'{total_warnings} relative import warnings')
else:
print('Imports are OK')


if __name__ == '__main__':
x = ImportChecker(os.path.dirname(os.path.dirname(__file__)))
x.check_imports()
4 changes: 4 additions & 0 deletions akmpt/build/config.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
build_name = main # build output name
build_type = onedir # the type of build, (auto|onefile|onedir)
run_integration_tests = False
run_unit_tests = False
6 changes: 6 additions & 0 deletions akmpt/build/get_bit_width.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import struct


def get_bit_width(interpreter_path):
size = struct.calcsize('P') * 8
return 'x86' if size == 32 else 'x64'

0 comments on commit 8a67b4f

Please sign in to comment.