2019-02-22 14:20:09 +11:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
|
|
|
"""
|
|
|
|
Runs an application in a manner that allows it to discover various engine
|
|
|
|
resources that may be in unexpected locations.
|
|
|
|
|
|
|
|
In particular: this allows running an application 'in-tree' under a debugger
|
|
|
|
with dependencies and resources out of line.
|
|
|
|
|
|
|
|
The base assumption is that this is probably a special circumstance and hence
|
|
|
|
should be run reasonably verbose but not interfere with a probable debugger
|
|
|
|
session.
|
|
|
|
"""
|
|
|
|
|
|
|
|
import platform
|
|
|
|
import os
|
|
|
|
import subprocess
|
|
|
|
import sys
|
|
|
|
|
|
|
|
# We need these ASAN options set so that nVidia's driver doesn't force an
|
|
|
|
# immediate halt.
|
|
|
|
#
|
|
|
|
# invalid_pointer_pairs gives overly verbose reports from deep within some
|
|
|
|
# core dependencies so it's not enabled by default.
|
|
|
|
asan_options = {
|
|
|
|
'protect_shadow_gap': 0,
|
|
|
|
'detect_stack_use_after_return': 1,
|
|
|
|
#'detect_invalid_pointer_pairs': 1,
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
# A path that contains our application, the resources, and the runtime
|
|
|
|
# configuration.
|
|
|
|
#
|
|
|
|
# It is probably assumed that the root will directly contain a 'config.json';
|
|
|
|
# but there's a definitely an assumption that we won't travel deeper into a
|
|
|
|
# hierarchy without foreknowledge that we're dealing with a good path, so don't
|
|
|
|
# set this to something stupid like '/'.
|
|
|
|
#
|
|
|
|
# By default we find the first path below us that contains a 'config.json'
|
|
|
|
# file under the assumption this script sits within a game directory structure.
|
|
|
|
|
|
|
|
def find_root(init, default):
|
|
|
|
cursor = init
|
|
|
|
|
|
|
|
while True:
|
|
|
|
if os.path.isfile(os.path.join(cursor, 'config.json')):
|
|
|
|
return cursor
|
|
|
|
|
|
|
|
parent = os.path.dirname(cursor)
|
|
|
|
if parent == cursor:
|
|
|
|
return default
|
|
|
|
|
|
|
|
cursor = parent
|
|
|
|
|
|
|
|
root = os.path.abspath(os.path.dirname(__file__))
|
|
|
|
root = find_root(root, default=root)
|
|
|
|
|
|
|
|
|
|
|
|
defaults = {
|
|
|
|
# don't break terrifically often, given we're probably running a debugger,
|
|
|
|
# but write a lot of information to the console.
|
|
|
|
'BREAK_LEVEL': 'CRITICAL',
|
|
|
|
'LOG_LEVEL': 'DEBUG',
|
|
|
|
|
|
|
|
'CL_VENDOR_IGNORE': 'intel64.icd',
|
|
|
|
|
|
|
|
'ROOT': root,
|
|
|
|
|
|
|
|
'ASAN_OPTIONS': ':'.join (f"{k}={v}" for k,v in asan_options.items())
|
|
|
|
}
|
|
|
|
|
|
|
|
# Overwrite our defaults with the current environment. This ensures a user can
|
|
|
|
# override any of the above variables easily from the commandline.
|
|
|
|
|
|
|
|
env = defaults.copy()
|
|
|
|
env.update(os.environ)
|
|
|
|
|
2019-06-28 10:47:29 +10:00
|
|
|
def host_platform(tuple: str) -> str:
|
|
|
|
return {
|
|
|
|
'x86_64-w64-mingw32': 'Windows'
|
|
|
|
}[tuple]
|
|
|
|
|
2019-06-06 13:28:38 +10:00
|
|
|
|
2019-06-28 10:47:29 +10:00
|
|
|
# Don't use `platform.system()` as MinGW will pretend to be its own platform,
|
|
|
|
# and cross compiling will give you CBUILD rather than CHOST.
|
|
|
|
def is_really_windows() -> bool:
|
|
|
|
return "@CMAKE_SYSTEM_NAME@" == "Windows"
|
2019-06-06 13:28:38 +10:00
|
|
|
|
|
|
|
|
|
|
|
# MSYS2 wants to fuck us over by supplying a CMake that gives something
|
|
|
|
# approximating native paths, and by _also_ providing an environment that
|
|
|
|
# cannot use these paths in places like PATH.
|
|
|
|
#
|
|
|
|
# Break the paths to conform to their expectations if we're under Windows.
|
|
|
|
def break_path_for_msys2(path):
|
|
|
|
if not is_really_windows():
|
|
|
|
return path
|
|
|
|
|
|
|
|
# Test if we have a path of the form "C:/foo" (as opposed to a
|
|
|
|
# relative path, or an MSYS2 path like "/c/foo")
|
|
|
|
if path[1] != ':':
|
|
|
|
return path
|
|
|
|
|
|
|
|
return '/' + path[0] + path[2:]
|
|
|
|
|
|
|
|
|
2019-02-22 14:20:09 +11:00
|
|
|
# Windows has its own unique rules for library lookups. We record the
|
|
|
|
# environment variable which effects path lookups and the separator and the
|
|
|
|
# target directory for Windows and for every single other system we're likely
|
|
|
|
# to ever deal with...
|
2019-06-06 13:28:38 +10:00
|
|
|
if is_really_windows():
|
2019-06-28 10:50:32 +10:00
|
|
|
separator = ';'
|
2019-10-26 13:04:03 +11:00
|
|
|
depsdir = [ 'bin', 'lib']
|
|
|
|
|
2019-09-24 08:41:04 +10:00
|
|
|
# Add the likely path to runtime dependencies for Linux/mingw.
|
|
|
|
#
|
|
|
|
# We want to promote this over the practice of copying DLLs into the
|
|
|
|
# `.wine/windows/system32/` directory because it reduces the chances of
|
|
|
|
# stale DLLs when we upgrade the compiler.
|
|
|
|
depsdir.append("/usr/lib/gcc/x86_64-w64-mingw32/@CMAKE_CXX_COMPILER_VERSION@/")
|
2019-06-28 10:50:32 +10:00
|
|
|
searchvar = 'WINEPATH'
|
|
|
|
env['WINEDEBUG'] = '-all'
|
2019-10-29 11:53:05 +11:00
|
|
|
|
|
|
|
# Indicate that we want a local wine prefix in the build directory.
|
|
|
|
arch = env.get('WINEARCH', 'win64')
|
|
|
|
if not 'WINEPREFIX' in env:
|
|
|
|
env['WINEPREFIX'] = f"@CMAKE_CURRENT_BINARY_DIR@/{arch}"
|
2019-02-22 14:20:09 +11:00
|
|
|
else:
|
|
|
|
separator = ':'
|
|
|
|
depsdir = ['lib', 'lib64']
|
|
|
|
searchvar = 'LD_LIBRARY_PATH'
|
|
|
|
|
2019-10-31 14:44:36 +11:00
|
|
|
depsdir = [ os.path.join("@CMAKE_CURRENT_BINARY_DIR@/deps/", dir) for dir in depsdir ]
|
2019-02-22 14:20:09 +11:00
|
|
|
|
|
|
|
# append the in-tree dependencies to the library path
|
|
|
|
search = separator.join(
|
2019-09-24 08:41:04 +10:00
|
|
|
break_path_for_msys2(i) for i in depsdir
|
2019-02-22 14:20:09 +11:00
|
|
|
)
|
|
|
|
|
|
|
|
if searchvar in env:
|
|
|
|
env[searchvar] = search + separator + env[searchvar]
|
|
|
|
else:
|
|
|
|
env[searchvar] = search
|
|
|
|
|
2019-06-28 14:22:22 +10:00
|
|
|
env['RESOURCE_PREPEND'] = "@COMPILED_RESOURCE_DIR@"
|
2020-01-20 13:21:37 +11:00
|
|
|
env['FBX_IGNORE_TODO'] = 1
|
2019-06-28 14:22:22 +10:00
|
|
|
|
2019-02-22 14:20:09 +11:00
|
|
|
# It's probably unnecessary to pipe std
|
|
|
|
res = subprocess.run(sys.argv[1:], env=env, stderr=sys.stderr, stdout=sys.stdout)
|
|
|
|
sys.exit(res.returncode)
|