Refactor JavaScript platform build script

This commit is contained in:
Leon Krause 2018-03-21 15:51:44 +01:00
parent 25800ffb0e
commit d8d9eea722
9 changed files with 105 additions and 218 deletions

View file

@ -62,11 +62,11 @@ custom_tools = ['default']
platform_arg = ARGUMENTS.get("platform", ARGUMENTS.get("p", False)) platform_arg = ARGUMENTS.get("platform", ARGUMENTS.get("p", False))
if (os.name == "posix"): if os.name == "nt" and (platform_arg == "android" or ARGUMENTS.get("use_mingw", False)):
pass custom_tools = ['mingw']
elif (os.name == "nt"): elif platform_arg == 'javascript':
if platform_arg == "android" or platform_arg == "javascript" or ARGUMENTS.get("use_mingw", False): # Use generic POSIX build toolchain for Emscripten.
custom_tools = ['mingw'] custom_tools = ['cc', 'c++', 'ar', 'link', 'textfile', 'zip']
env_base = Environment(tools=custom_tools) env_base = Environment(tools=custom_tools)
if 'TERM' in os.environ: if 'TERM' in os.environ:

View file

@ -781,13 +781,10 @@ def use_windows_spawn_fix(self, platform=None):
import subprocess import subprocess
def mySubProcess(cmdline, env): def mySubProcess(cmdline, env):
prefix = ""
if(platform == 'javascript'):
prefix = "python.exe "
startupinfo = subprocess.STARTUPINFO() startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
proc = subprocess.Popen(prefix + cmdline, stdin=subprocess.PIPE, stdout=subprocess.PIPE, proc = subprocess.Popen(cmdline, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.PIPE, startupinfo=startupinfo, shell=False, env=env) stderr=subprocess.PIPE, startupinfo=startupinfo, shell=False, env=env)
data, err = proc.communicate() data, err = proc.communicate()
rv = proc.wait() rv = proc.wait()

View file

@ -3,38 +3,35 @@
Import('env') Import('env')
javascript_files = [ javascript_files = [
"os_javascript.cpp", 'audio_driver_javascript.cpp',
"audio_driver_javascript.cpp", 'http_client_javascript.cpp',
"javascript_main.cpp", 'javascript_eval.cpp',
"power_javascript.cpp", 'javascript_main.cpp',
"http_client_javascript.cpp", 'os_javascript.cpp',
"javascript_eval.cpp",
] ]
env_javascript = env.Clone() build = env.add_program(['#bin/godot${PROGSUFFIX}.js', '#bin/godot${PROGSUFFIX}.wasm'], javascript_files);
if env['target'] == "profile":
env_javascript.Append(CPPFLAGS=['-DPROFILER_ENABLED'])
javascript_objects = []
for x in javascript_files:
javascript_objects.append(env_javascript.Object(x))
env.Append(LINKFLAGS=["-s", "EXPORTED_FUNCTIONS=\"['_main','_main_after_fs_sync','_send_notification']\""])
target_dir = env.Dir("#bin")
build = env.add_program(['#bin/godot', target_dir.File('godot' + env['PROGSUFFIX'] + '.wasm')], javascript_objects, PROGSUFFIX=env['PROGSUFFIX'] + '.js');
js, wasm = build js, wasm = build
js_libraries = [] js_libraries = [
js_libraries.append(env.File('http_request.js')) 'http_request.js',
]
for lib in js_libraries: for lib in js_libraries:
env.Append(LINKFLAGS=['--js-library', lib.path]) env.Append(LINKFLAGS=['--js-library', env.File(lib).path])
env.Depends(build, js_libraries) env.Depends(build, js_libraries)
wrapper_start = env.File('pre.js') wrapper_start = env.File('pre.js')
wrapper_end = env.File('engine.js') wrapper_end = env.File('engine.js')
js_final = env.Textfile('#bin/godot', [wrapper_start, js, wrapper_end], TEXTFILESUFFIX=env['PROGSUFFIX'] + '.wrapped.js') js_wrapped = env.Textfile('#bin/godot', [wrapper_start, js, wrapper_end], TEXTFILESUFFIX='${PROGSUFFIX}.wrapped.js')
zip_dir = target_dir.Dir('.javascript_zip') zip_dir = env.Dir('#bin/.javascript_zip')
zip_files = env.InstallAs([zip_dir.File('godot.js'), zip_dir.File('godot.wasm'), zip_dir.File('godot.html')], [js_final, wasm, '#misc/dist/html/default.html']) zip_files = env.InstallAs([
Zip('#bin/godot', zip_files, ZIPSUFFIX=env['PROGSUFFIX'] + env['ZIPSUFFIX'], ZIPROOT=zip_dir, ZIPCOMSTR="Archving $SOURCES as $TARGET") zip_dir.File('godot.js'),
zip_dir.File('godot.wasm'),
zip_dir.File('godot.html')
], [
js_wrapped,
wasm,
'#misc/dist/html/default.html'
])
env.Zip('#bin/godot', zip_files, ZIPROOT=zip_dir, ZIPSUFFIX='${PROGSUFFIX}${ZIPSUFFIX}', ZIPCOMSTR='Archving $SOURCES as $TARGET')

View file

@ -8,23 +8,22 @@ def is_active():
def get_name(): def get_name():
return "JavaScript" return 'JavaScript'
def can_build(): def can_build():
return 'EMSCRIPTEN_ROOT' in os.environ or 'EMSCRIPTEN' in os.environ
return ("EMSCRIPTEN_ROOT" in os.environ or "EMSCRIPTEN" in os.environ)
def get_opts(): def get_opts():
from SCons.Variables import BoolVariable from SCons.Variables import BoolVariable
return [ return [
# eval() can be a security concern, so it can be disabled.
BoolVariable('javascript_eval', 'Enable JavaScript eval interface', True), BoolVariable('javascript_eval', 'Enable JavaScript eval interface', True),
] ]
def get_flags(): def get_flags():
return [ return [
('tools', False), ('tools', False),
('module_theora_enabled', False), ('module_theora_enabled', False),
@ -36,24 +35,11 @@ def get_flags():
] ]
def create(env):
# remove Windows' .exe suffix
return env.Clone(tools=['textfile', 'zip'], PROGSUFFIX='')
def escape_sources_backslashes(target, source, env, for_signature):
return [path.replace('\\','\\\\') for path in env.GetBuildPath(source)]
def escape_target_backslashes(target, source, env, for_signature):
return env.GetBuildPath(target[0]).replace('\\','\\\\')
def configure(env): def configure(env):
## Build type ## Build type
if (env["target"] == "release"): if env['target'] == 'release' or env['target'] == 'profile':
# Use -Os to prioritize optimizing for reduced file size. This is # Use -Os to prioritize optimizing for reduced file size. This is
# particularly valuable for the web platform because it directly # particularly valuable for the web platform because it directly
# decreases download time. # decreases download time.
@ -62,66 +48,99 @@ def configure(env):
# run-time performance. # run-time performance.
env.Append(CCFLAGS=['-Os']) env.Append(CCFLAGS=['-Os'])
env.Append(LINKFLAGS=['-Os']) env.Append(LINKFLAGS=['-Os'])
if env['target'] == 'profile':
env.Append(LINKFLAGS=['--profiling-funcs'])
elif (env["target"] == "release_debug"): elif env['target'] == 'release_debug':
env.Append(CCFLAGS=['-O2', '-DDEBUG_ENABLED']) env.Append(CPPDEFINES=['DEBUG_ENABLED'])
env.Append(CCFLAGS=['-O2'])
env.Append(LINKFLAGS=['-O2']) env.Append(LINKFLAGS=['-O2'])
# retain function names at the cost of file size, for backtraces and profiling # Retain function names for backtraces at the cost of file size.
env.Append(LINKFLAGS=['--profiling-funcs']) env.Append(LINKFLAGS=['--profiling-funcs'])
elif (env["target"] == "debug"): elif env['target'] == 'debug':
env.Append(CCFLAGS=['-O1', '-D_DEBUG', '-g', '-DDEBUG_ENABLED']) env.Append(CPPDEFINES=['DEBUG_ENABLED'])
env.Append(CCFLAGS=['-O1', '-g'])
env.Append(LINKFLAGS=['-O1', '-g']) env.Append(LINKFLAGS=['-O1', '-g'])
env.Append(LINKFLAGS=['-s', 'ASSERTIONS=1']) env.Append(LINKFLAGS=['-s', 'ASSERTIONS=1'])
## Compiler configuration ## Compiler configuration
env['ENV'] = os.environ env['ENV'] = os.environ
if ("EMSCRIPTEN_ROOT" in os.environ): if 'EMSCRIPTEN_ROOT' in os.environ:
env.PrependENVPath('PATH', os.environ['EMSCRIPTEN_ROOT']) env.PrependENVPath('PATH', os.environ['EMSCRIPTEN_ROOT'])
elif ("EMSCRIPTEN" in os.environ): elif 'EMSCRIPTEN' in os.environ:
env.PrependENVPath('PATH', os.environ['EMSCRIPTEN']) env.PrependENVPath('PATH', os.environ['EMSCRIPTEN'])
env['CC'] = 'emcc'
env['CXX'] = 'em++' env['CC'] = 'emcc'
env['LINK'] = 'emcc' env['CXX'] = 'em++'
env['RANLIB'] = 'emranlib' env['LINK'] = 'emcc'
# Emscripten's ar has issues with duplicate file names, so use cc
env['AR'] = 'emcc' # Emscripten's ar has issues with duplicate file names, so use cc.
env['AR'] = 'emcc'
env['ARFLAGS'] = '-o' env['ARFLAGS'] = '-o'
# emranlib is a noop, so it's safe to use with AR=emcc.
env['RANLIB'] = 'emranlib'
if (os.name == 'nt'): # Use TempFileMunge since some AR invocations are too long for cmd.exe.
# use TempFileMunge on Windows since some commands get too long for # Use POSIX-style paths, required with TempFileMunge.
# cmd.exe even with spawn_fix env['ARCOM_POSIX'] = env['ARCOM'].replace(
# need to escape backslashes for this '$TARGET', '$TARGET.posix').replace(
env['ESCAPED_SOURCES'] = escape_sources_backslashes '$SOURCES', '$SOURCES.posix')
env['ESCAPED_TARGET'] = escape_target_backslashes env['ARCOM'] = '${TEMPFILE(ARCOM_POSIX)}'
env['ARCOM'] = '${TEMPFILE("%s")}' % env['ARCOM'].replace('$SOURCES', '$ESCAPED_SOURCES').replace('$TARGET', '$ESCAPED_TARGET')
# All intermediate files are just LLVM bitcode.
env['OBJPREFIX'] = ''
env['OBJSUFFIX'] = '.bc' env['OBJSUFFIX'] = '.bc'
env['PROGPREFIX'] = ''
# Program() output consists of multiple files, so specify suffixes manually at builder.
env['PROGSUFFIX'] = ''
env['LIBPREFIX'] = 'lib'
env['LIBSUFFIX'] = '.bc' env['LIBSUFFIX'] = '.bc'
env['LIBPREFIXES'] = ['$LIBPREFIX']
env['LIBSUFFIXES'] = ['$LIBSUFFIX']
## Compile flags ## Compile flags
env.Append(CPPPATH=['#platform/javascript']) env.Append(CPPPATH=['#platform/javascript'])
env.Append(CPPFLAGS=['-DJAVASCRIPT_ENABLED', '-DUNIX_ENABLED', '-DTYPED_METHOD_BIND', '-DNO_THREADS']) env.Append(CPPDEFINES=['JAVASCRIPT_ENABLED', 'UNIX_ENABLED'])
env.Append(CPPFLAGS=['-DGLES3_ENABLED'])
# These flags help keep the file size down # No multi-threading (SharedArrayBuffer) available yet,
env.Append(CPPFLAGS=["-fno-exceptions", '-DNO_SAFE_CAST', '-fno-rtti']) # once feasible also consider memory buffer size issues.
env.Append(CPPDEFINES=['NO_THREADS'])
# These flags help keep the file size down.
env.Append(CCFLAGS=['-fno-exceptions', '-fno-rtti'])
# Don't use dynamic_cast, necessary with no-rtti.
env.Append(CPPDEFINES=['NO_SAFE_CAST'])
if env['javascript_eval']: if env['javascript_eval']:
env.Append(CPPFLAGS=['-DJAVASCRIPT_EVAL_ENABLED']) env.Append(CPPDEFINES=['JAVASCRIPT_EVAL_ENABLED'])
## Link flags ## Link flags
env.Append(LINKFLAGS=['-s', 'BINARYEN=1']) env.Append(LINKFLAGS=['-s', 'BINARYEN=1'])
# Allow increasing memory buffer size during runtime. This is efficient
# when using WebAssembly (in comparison to asm.js) and works well for
# us since we don't know requirements at compile-time.
env.Append(LINKFLAGS=['-s', 'ALLOW_MEMORY_GROWTH=1']) env.Append(LINKFLAGS=['-s', 'ALLOW_MEMORY_GROWTH=1'])
# This setting just makes WebGL 2 APIs available, it does NOT disable WebGL 1.
env.Append(LINKFLAGS=['-s', 'USE_WEBGL2=1']) env.Append(LINKFLAGS=['-s', 'USE_WEBGL2=1'])
env.Append(LINKFLAGS=['-s', 'EXTRA_EXPORTED_RUNTIME_METHODS="[\'FS\']"'])
# engine.js uses FS but is not currently evaluated by Emscripten, so export FS.
# TODO: Getting rid of this export is desirable.
extra_exports = [
'FS',
]
env.Append(LINKFLAGS=['-s', 'EXTRA_EXPORTED_RUNTIME_METHODS="%s"' % repr(extra_exports)])
env.Append(LINKFLAGS=['-s', 'INVOKE_RUN=0']) env.Append(LINKFLAGS=['-s', 'INVOKE_RUN=0'])
# TODO: Reevaluate usage of this setting now that engine.js manages engine runtime.
env.Append(LINKFLAGS=['-s', 'NO_EXIT_RUNTIME=1']) env.Append(LINKFLAGS=['-s', 'NO_EXIT_RUNTIME=1'])
# TODO: Move that to opus module's config # TODO: Move that to opus module's config.
if 'module_opus_enabled' in env and env['module_opus_enabled']: if 'module_opus_enabled' in env and env['module_opus_enabled']:
env.opus_fixed_point = "yes" env.opus_fixed_point = 'yes'

View file

@ -40,7 +40,7 @@ static void main_loop() {
os->main_loop_iterate(); os->main_loop_iterate();
} }
extern "C" void main_after_fs_sync(char *p_idbfs_err) { extern "C" EMSCRIPTEN_KEEPALIVE void main_after_fs_sync(char *p_idbfs_err) {
String idbfs_err = String::utf8(p_idbfs_err); String idbfs_err = String::utf8(p_idbfs_err);
if (!idbfs_err.empty()) { if (!idbfs_err.empty()) {

View file

@ -413,14 +413,13 @@ static EM_BOOL joy_callback_func(int p_type, const EmscriptenGamepadEvent *p_eve
return false; return false;
} }
extern "C" { extern "C" EMSCRIPTEN_KEEPALIVE void send_notification(int notif) {
void send_notification(int notif) {
if (notif == MainLoop::NOTIFICATION_WM_MOUSE_ENTER || notif == MainLoop::NOTIFICATION_WM_MOUSE_EXIT) { if (notif == MainLoop::NOTIFICATION_WM_MOUSE_ENTER || notif == MainLoop::NOTIFICATION_WM_MOUSE_EXIT) {
_cursor_inside_canvas = notif == MainLoop::NOTIFICATION_WM_MOUSE_ENTER; _cursor_inside_canvas = notif == MainLoop::NOTIFICATION_WM_MOUSE_ENTER;
} }
OS_JavaScript::get_singleton()->get_main_loop()->notification(notif); OS_JavaScript::get_singleton()->get_main_loop()->notification(notif);
} }
}
Error OS_JavaScript::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) { Error OS_JavaScript::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) {
@ -480,8 +479,6 @@ Error OS_JavaScript::initialize(const VideoMode &p_desired, int p_video_driver,
input = memnew(InputDefault); input = memnew(InputDefault);
_input = input; _input = input;
power_manager = memnew(PowerJavascript);
#define EM_CHECK(ev) \ #define EM_CHECK(ev) \
if (result != EMSCRIPTEN_RESULT_SUCCESS) \ if (result != EMSCRIPTEN_RESULT_SUCCESS) \
ERR_PRINTS("Error while setting " #ev " callback: Code " + itos(result)) ERR_PRINTS("Error while setting " #ev " callback: Code " + itos(result))
@ -968,15 +965,21 @@ String OS_JavaScript::get_joy_guid(int p_device) const {
} }
OS::PowerState OS_JavaScript::get_power_state() { OS::PowerState OS_JavaScript::get_power_state() {
return power_manager->get_power_state();
WARN_PRINT("Power management is not supported for the HTML5 platform, defaulting to POWERSTATE_UNKNOWN");
return OS::POWERSTATE_UNKNOWN;
} }
int OS_JavaScript::get_power_seconds_left() { int OS_JavaScript::get_power_seconds_left() {
return power_manager->get_power_seconds_left();
WARN_PRINT("Power management is not supported for the HTML5 platform, defaulting to -1");
return -1;
} }
int OS_JavaScript::get_power_percent_left() { int OS_JavaScript::get_power_percent_left() {
return power_manager->get_power_percent_left();
WARN_PRINT("Power management is not supported for the HTML5 platform, defaulting to -1");
return -1;
} }
bool OS_JavaScript::_check_internal_feature_support(const String &p_feature) { bool OS_JavaScript::_check_internal_feature_support(const String &p_feature) {

View file

@ -36,7 +36,6 @@
#include "main/input_default.h" #include "main/input_default.h"
#include "os/input.h" #include "os/input.h"
#include "os/main_loop.h" #include "os/main_loop.h"
#include "power_javascript.h"
#include "servers/audio_server.h" #include "servers/audio_server.h"
#include "servers/visual/rasterizer.h" #include "servers/visual/rasterizer.h"
@ -64,8 +63,6 @@ class OS_JavaScript : public OS_Unix {
GetUserDataDirFunc get_user_data_dir_func; GetUserDataDirFunc get_user_data_dir_func;
PowerJavascript *power_manager;
static void _close_notification_funcs(const String &p_file, int p_flags); static void _close_notification_funcs(const String &p_file, int p_flags);
void process_joypads(); void process_joypads();

View file

@ -1,73 +0,0 @@
/*************************************************************************/
/* power_javascript.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "power_javascript.h"
#include "error_macros.h"
bool PowerJavascript::UpdatePowerInfo() {
// TODO Javascript implementation
return false;
}
OS::PowerState PowerJavascript::get_power_state() {
if (UpdatePowerInfo()) {
return power_state;
} else {
WARN_PRINT("Power management is not implemented on this platform, defaulting to POWERSTATE_UNKNOWN");
return OS::POWERSTATE_UNKNOWN;
}
}
int PowerJavascript::get_power_seconds_left() {
if (UpdatePowerInfo()) {
return nsecs_left;
} else {
WARN_PRINT("Power management is not implemented on this platform, defaulting to -1");
return -1;
}
}
int PowerJavascript::get_power_percent_left() {
if (UpdatePowerInfo()) {
return percent_left;
} else {
WARN_PRINT("Power management is not implemented on this platform, defaulting to -1");
return -1;
}
}
PowerJavascript::PowerJavascript() :
nsecs_left(-1),
percent_left(-1),
power_state(OS::POWERSTATE_UNKNOWN) {
}
PowerJavascript::~PowerJavascript() {
}

View file

@ -1,53 +0,0 @@
/*************************************************************************/
/* power_javascript.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef PLATFORM_JAVASCRIPT_POWER_JAVASCRIPT_H_
#define PLATFORM_JAVASCRIPT_POWER_JAVASCRIPT_H_
#include "os/os.h"
class PowerJavascript {
private:
int nsecs_left;
int percent_left;
OS::PowerState power_state;
bool UpdatePowerInfo();
public:
PowerJavascript();
virtual ~PowerJavascript();
OS::PowerState get_power_state();
int get_power_seconds_left();
int get_power_percent_left();
};
#endif /* PLATFORM_JAVASCRIPT_POWER_JAVASCRIPT_H_ */