Alexander Kraus 94589eb2eb
feat(examples): add example playing sointu tracks from Python (#108)
* Added Python code example.
* Added pyinstaller build.
* Clarified debugging steps in README.md.
* Added linux implementation.
* Cosmetics.
* Updated README with correct steps.
2023-10-11 09:37:00 +03:00

150 lines
4.5 KiB
Python

from distutils.command.build_ext import build_ext
from distutils.errors import (
CCompilerError,
DistutilsExecError,
DistutilsPlatformError,
)
from distutils.core import Extension
from os.path import (
dirname,
join,
abspath,
exists,
basename,
splitext,
)
from os import mkdir
from subprocess import run
from platform import system
from sys import exit
track = "../../patches/physics_girl_st.yml"
class BuildFailed(Exception):
pass
class ExtBuilder(build_ext):
def run(self):
try:
build_ext.run(self)
except (DistutilsPlatformError, FileNotFoundError):
raise BuildFailed('File not found. Could not compile C extension.')
def build_extension(self, ext):
try:
build_ext.build_extension(self, ext)
except (CCompilerError, DistutilsExecError, DistutilsPlatformError, ValueError):
raise BuildFailed('Could not compile C extension.')
def build(setup_kwargs):
# Make sure the build directory exists and setup the
# relative paths correctly.
cwd = abspath(".")
print("Running from:", cwd)
current_source_dir = abspath(dirname(__file__))
project_source_dir = abspath(join(current_source_dir, "..", "..", ".."))
current_binary_dir = join(current_source_dir, 'build')
if not exists(current_binary_dir):
mkdir(current_binary_dir)
host_is_windows = system() == "Windows"
executable_suffix = ".exe" if host_is_windows else ""
object_suffix = ".obj" if host_is_windows else ".o"
# Build the sointu compiler first.
compiler_executable = join(current_binary_dir, "sointu-compile{}".format(executable_suffix))
result = run(
args=[
"go", "build",
"-o", compiler_executable,
"cmd/sointu-compile/main.go",
],
cwd=project_source_dir,
shell=True if host_is_windows else False,
)
if result.returncode != 0:
print("sointu-compile build process exited with:", result.returncode)
print(result.stdout)
exit(1)
track_file_name = abspath(join(current_source_dir, track))
(track_name_base, _) = splitext(basename(track_file_name))
print("Compiling track:", track_file_name)
# Compile the track.
sointu_compiler_arch = "amd64"
track_asm_file = join(current_binary_dir, '{}.asm'.format(track_name_base))
result = run(
args=[
compiler_executable,
"-o", track_asm_file,
"-arch={}".format(sointu_compiler_arch),
track_file_name,
],
)
if result.returncode != 0:
print("sointu-compile process exited with:", result.returncode)
print(result.stdout)
exit(1)
# Assemble the track.
nasm_abi = "Win64" if host_is_windows else "Elf64"
track_object_file = join(current_binary_dir, '{}{}'.format(track_name_base, object_suffix))
print("Assembling track asm source:", track_asm_file)
result = run(
args=[
'nasm',
'-o', track_object_file,
'-f', nasm_abi,
track_asm_file,
],
)
if result.returncode != 0:
print("nasm process exited with:", result.returncode)
print(result.stdout)
exit(1)
# Export the plugin.
print("Linking object file into Python extension module:", track_object_file)
setup_kwargs.update({
"ext_modules": [
Extension(
"sointu",
include_dirs=[
current_binary_dir,
current_source_dir,
],
sources=[
"sointu.c",
],
extra_compile_args=[
"-DTRACK_HEADER=\"{}.h\"".format(track_name_base),
] + ([
"-DWIN32",
] if host_is_windows else [
"-DUNIX",
"-fPIC",
]),
extra_objects=[
track_object_file,
],
extra_link_args=[
"dsound.lib",
"ws2_32.lib",
"ucrt.lib",
"user32.lib",
] if host_is_windows else [
"-z", "noexecstack",
"--no-pie",
"-lasound",
"-lpthread",
"-lpython3.11",
],
),
],
"cmdclass": {
"build_ext": ExtBuilder,
},
})