diff --git a/Installer/package.txt b/Installer/package.txt index d7b7beb8b8f..f7d80ab7a6a 100644 --- a/Installer/package.txt +++ b/Installer/package.txt @@ -602,11 +602,17 @@ component Runtime.MacOSX component Runtime.Emscripten into "[[ToolsFolder]]/Runtime/Emscripten/js" place - file emscripten:standalone[[BaseEditionTagLower]]-[[VersionTag]].html - file emscripten:standalone[[BaseEditionTagLower]]-[[VersionTag]].html.mem - file emscripten:standalone[[BaseEditionTagLower]]-[[VersionTag]].js - file emscripten:emscripten-startup-template.livecodescript - rfolder emscripten:emscripten-standalone-template + file emscripten-js:standalone[[BaseEditionTagLower]]-[[VersionTag]].html + file emscripten-js:standalone[[BaseEditionTagLower]]-[[VersionTag]].html.mem + file emscripten-js:standalone[[BaseEditionTagLower]]-[[VersionTag]].js + file emscripten-js:emscripten-startup-template.livecodescript + rfolder emscripten-js:emscripten-standalone-template + into "[[ToolsFolder]]/Runtime/Emscripten/wasm" place + file emscripten-wasm:standalone[[BaseEditionTagLower]]-[[VersionTag]].html + file emscripten-wasm:standalone[[BaseEditionTagLower]]-[[VersionTag]].js + file emscripten-wasm:standalone[[BaseEditionTagLower]]-[[VersionTag]].wasm + file emscripten-wasm:emscripten-startup-template.livecodescript + rfolder emscripten-wasm:emscripten-standalone-template ////////// diff --git a/Makefile b/Makefile index 8b3e0b9d16f..89568d49800 100644 --- a/Makefile +++ b/Makefile @@ -276,15 +276,21 @@ all-win-x86_64: # Emscripten rules ################################################################ -config-emscripten: - $(EMMAKE) ./config.sh --platform emscripten +config-emscripten-%: + $(EMMAKE) ./config.sh --platform emscripten-$* -compile-emscripten: - $(EMMAKE) $(MAKE) -C build-emscripten/livecode default +compile-emscripten-%: + $(EMMAKE) $(MAKE) -C build-emscripten-$*/livecode default -check-emscripten: - $(EMMAKE) $(MAKE) -C build-emscripten/livecode check +check-emscripten-%: + $(EMMAKE) $(MAKE) -C build-emscripten-$*/livecode check -all-emscripten: - $(MAKE) config-emscripten - $(MAKE) compile-emscripten +all-emscripten-%: + $(MAKE) config-emscripten-$* + $(MAKE) compile-emscripten-$* + +#synonyms for *-emscripten-js +all-emscripten: all-emscripten-js +config-emscripten: config-emscripten-js +compile-emscripten: compile-emscripten-js +check-emscripten: check-emscripten-js diff --git a/buildbot.py b/buildbot.py index 3b2d16d4465..763a94e4033 100755 --- a/buildbot.py +++ b/buildbot.py @@ -74,6 +74,11 @@ def get_build_platform(): os.environ.get('BUILD_SUBPLATFORM')) if platform[0] is None: error('You must set $BUILD_PLATFORM') + + # Assume js architecture for emscripten platform if not specified + if platform[0] is 'emscripten': + platform[0] = 'emscripten-js' + return platform def get_buildtype(): diff --git a/config.py b/config.py index b217626d9f1..e4fc8b8b4f0 100755 --- a/config.py +++ b/config.py @@ -42,6 +42,7 @@ 'x86-win32', # TODO[2017-03-23] More specific ABI 'x86_64-win32', 'js-emscripten-sdk1.35', + 'wasm-emscripten-sdk1.39', ) KNOWN_PLATFORMS = ( @@ -49,7 +50,7 @@ 'android-armv6', 'android-armv7', 'android-arm64', 'android-x86', 'android-x86_64', 'mac', 'ios', 'win-x86', 'win-x86_64', - 'emscripten' + 'emscripten', 'emscripten-js', 'emscripten-wasm', ) def usage(exit_status): @@ -288,10 +289,14 @@ def validate_target_arch(opts): validate_platform(opts) platform = opts['PLATFORM'] - if platform == 'emscripten': + if platform in ['emscripten', 'emscripten-js']: opts['TARGET_ARCH'] = 'js' opts['UNIFORM_ARCH'] = opts['TARGET_ARCH'] return + if platform == 'emscripten-wasm': + opts['TARGET_ARCH'] = 'wasm' + opts['UNIFORM_ARCH'] = opts['TARGET_ARCH'] + return platform_arch = re.search('-(x86|x86_64|arm(64|v(6(hf)?|7)))$', platform) if platform_arch is not None: diff --git a/config/arch.gypi b/config/arch.gypi index 2f7c4540449..37d2522bb6c 100644 --- a/config/arch.gypi +++ b/config/arch.gypi @@ -57,12 +57,6 @@ 'host_os': 'linux', }, ], - [ - 'OS == "emscripten"', - { - 'target_arch': 'js', - }, - ], ], }, diff --git a/config/emscripten-settings.gypi b/config/emscripten-settings.gypi index 9fed76bafbe..55dbde9c786 100644 --- a/config/emscripten-settings.gypi +++ b/config/emscripten-settings.gypi @@ -22,8 +22,6 @@ '-s ALLOW_MEMORY_GROWTH=1', '-s ASSERTIONS=1', '-s DEMANGLE_SUPPORT=1', - '-s EMTERPRETIFY=1', - '-s EMTERPRETIFY_ASYNC=1', '-s LINKABLE=1', '-s RESERVED_FUNCTION_POINTERS=1024', '-s TOTAL_MEMORY=67108864', @@ -46,6 +44,23 @@ 'target_conditions': [ + [ + 'toolset_arch == "wasm"', + { + 'cflags': + [ + '-s ASYNCIFY=1', + '-s', 'ASYNCIFY_IMPORTS=["MCEmscriptenAsyncYield"]', + ], + }, + { + 'cflags': + [ + '-s EMTERPRETIFY=1', + '-s EMTERPRETIFY_ASYNC=1', + ] + }, + ], [ 'silence_warnings == 0', { @@ -101,7 +116,7 @@ { 'cflags': [ - '-Os', + '-O2', '-g3', ], diff --git a/config/emscripten.gypi b/config/emscripten.gypi index 932eae86092..0e3b7db7451 100644 --- a/config/emscripten.gypi +++ b/config/emscripten.gypi @@ -2,7 +2,7 @@ 'variables': { 'mobile': 1, - 'output_dir': '../emscripten-bin', + 'output_dir': '../emscripten-<(target_arch)-bin', }, 'target_defaults': diff --git a/docs/development/build-emscripten.md b/docs/development/build-emscripten.md index 74f062406d3..6c96c86085f 100644 --- a/docs/development/build-emscripten.md +++ b/docs/development/build-emscripten.md @@ -2,7 +2,7 @@ ![LiveCode Community Logo](http://livecode.com/wp-content/uploads/2015/02/livecode-logo.png) -Copyright © 2015 LiveCode Ltd., Edinburgh, UK +Copyright © 2020 LiveCode Ltd., Edinburgh, UK **Warning**: Emscripten (HTML5) platform support for LiveCode is experimental and not recommended for production use. @@ -11,57 +11,121 @@ Copyright © 2015 LiveCode Ltd., Edinburgh, UK You will need a 64-bit Linux machine or VM with at least 4 GB of RAM (8 GB is recommended). +## Emscripten build architectures + +There are two emscripten architectures that can be targeted for building the Emscripten engine, asm.js and WebAssembly. asm.js is based on a subset of JavaScript, whereas WebAssembly produces binary byte-code. + +## Building for asm.js + +The emscripten-js build target produces output in asm.js format that is converted to bytecode in order to be run through an interpreter. The emterpreted code can then be halted and resumed. This method of producing interruptable code is now deprecated but the tools are still available from the Emscripten SDK. + ### Emscripten SDK -Unsurprisingly, the Emscripten SDK must be installed in order to build -an Emscripten engine. +1. Download the Emscripten SDK. The recommended method for obtaining the Emscripten SDK is to use git tools to clone the emsdk repository from github. Other methods are described in . Put it in `/opt/emsdk_js`, for example. -1. Download the portable Emscripten SDK from . Put it in `/opt/emsdk_portable`, for example. + `git clone https://github.com/emscripten-core/emsdk /opt/emsdk_js` -2. Check which SDKs are available by running: +2. Check that the required tools are available by running: - /opt/emsdk_portable/emsdk list + `/opt/emsdk_js/emsdk list --old` 3. Install and activate SDK 1.35.23 by running: - /opt/emsdk_portable/emsdk install sdk-1.35.23-32bit - /opt/emsdk_portable/emsdk activate sdk-1.35.23-32bit + `/opt/emsdk_js/emsdk install node-12.9.1-64bit` + `/opt/emsdk_js/emsdk activate --embedded node-12.9.1-64bit` + `/opt/emsdk_js/emsdk install emscripten-tag-1.35.23-64bit` + `/opt/emsdk_js/emsdk activate --embedded emscripten-tag-1.35.23-64bit` + `/opt/emsdk_js/emsdk install fastcomp-clang-tag-e1.35.23-64bit` + `/opt/emsdk_js/emsdk activate --embedded fastcomp-clang-tag-e1.35.23-64bit` This will take a really long time and use an insane amount of RAM. -## Build environment +### Build environment + +Before building for Emscripten, source the Emscripten SDK script that sets up the environment correctly. You need to source it with the `.` or `source` command rather than just running it. + + `source /opt/emsdk_js/emsdk_env.sh` + +### Configuring LiveCode + +To configure LiveCode, run: + + `make config-emscripten-js` + +This will generate make control files in the `build-emscripten-js` directory. You can also run `config.sh` directly. + +### Compiling LiveCode + +To compile LiveCode, run: + + `make compile-emscripten-js` + +This will generate outputs in the `emscripten-bin-js` directory. + +## Building for WebAssembly + +The emscripten-wasm build target produces output in WebAssembly format that is then processed to allow the call stack to be saved and restored, making it possible for the engine execution to be halted and resumed. This is now the preferred method of generating interruptable code and can be built with the most recent version of the Emscripten SDK tools (version 1.39.12 as of April 2020). + +### Emscripten SDK + +1. Download the Emscripten SDK as described above. Put it in `/opt/emsdk_wasm`, for example. + + `git clone https://github.com/emscripten-core/emsdk /opt/emsdk_wasm` + +2. Check that the required tools are available by running: + + `/opt/emsdk_wasm/emsdk list` + +3. Install and activate SDK 1.39.12 by running: + + `/opt/emsdk_wasm/emsdk install 1.39.12` + `/opt/emsdk_wasm/emsdk activate --embedded 1.39.12` + +### Build environment Before building for Emscripten, source the Emscripten SDK script that sets up the environment correctly. You need to source it with the `.` or `source` command rather than just running it. - source /opt/emsdk_portable/emsdk_env.sh + `source /opt/emsdk_wasm/emsdk_env.sh` -## Configuring LiveCode +### Configuring LiveCode To configure LiveCode, run: - make config-emscripten + `make config-emscripten-wasm` -This will generate make control files in the `build-emscripten` directory. You can also run `config.sh` directly. +This will generate make control files in the `build-emscripten-wasm` directory. You can also run `config.sh` directly. -## Compiling LiveCode +### Compiling LiveCode To compile LiveCode, run: - make compile-emscripten + `make compile-emscripten-wasm` -This will generate outputs in the `emscripten-bin` directory. +This will generate outputs in the `emscripten-bin-wasm` directory. ## Running LiveCode **Note**: See also the "HTML5 Deployment" guide, available in the in-IDE dictionary. +A desktop IDE built and launched from the repository checkout folder can be used to run HTML5 standalones with the emscripten engines compiled using the steps above. + +1. Enable emscripten output in the standalone settings for your stack. You can choose which architectures to include in the standalone output folder. + +2. Select a browser from the "Development -> Test Target" menu + +3. Click the "Test" button on the IDE toolbar. + +Your stack will be compiled to a HTML5 standalone and launched in the selected browser. If "WebAssembly" output is enabled in the standalone settings of the stack then the WebAssembly engine will be used when compiling the test app, otherwise the asm.js engine will be used. + +## Manual testing of HTML5 standalones. + Use the desktop build of the LiveCode IDE to run the standalone builder and create an "HTML5" standalone. Once you've created a standalone, you can open the HTML file in a web browser to try out the engine. Some web browsers (including Google Chrome) have JavaScript security policies that won't allow you to run the engine from a local filesystem. For these browsers, you will need to run a local web server. You can use the following steps to launch a local-only webserver listening on port 8080: - cd /path/to/my/standalone - python -m SimpleHTTPServer 8080 + `cd /path/to/my/standalone` + `python -m SimpleHTTPServer 8080` You can then load http://localhost:8080/ in a web browser to view your standalone HTML5 engine. diff --git a/docs/notes/feature-emscripten-webassembly.md b/docs/notes/feature-emscripten-webassembly.md new file mode 100644 index 00000000000..e51b21983b3 --- /dev/null +++ b/docs/notes/feature-emscripten-webassembly.md @@ -0,0 +1,14 @@ +# New WebAssembly option for HTML5 deployment of LiveCode apps (experimental) + +The LiveCode engine can now be deployed to HTML5 as WebAssembly. +WebAssembly is supported in most major web browsers and allows HTML5 +apps to run at near-native speeds. + +To used WebAssembly when deploying a stack as an HTML5 application, +enable the "Build for WebAssembly" checkbox on the "HTML5" page of the +standalone settings window, and then generate the standalone in the +normal way. + +For more information on HTML5 deployment, including options for +embedding LiveCode standalones in web pages, please see the "HTML5 +Deployment" guide in the IDE dictionary. diff --git a/engine/engine.gyp b/engine/engine.gyp index 25eada469d6..ca62900e8ea 100755 --- a/engine/engine.gyp +++ b/engine/engine.gyp @@ -508,21 +508,6 @@ [ 'OS == "emscripten"', { - 'all_dependent_settings': - { - 'variables': - { - 'dist_aux_files': - [ - 'rsrc/emscripten-standalone-template/', - 'rsrc/emscripten-startup-template.livecodescript/', - '<(PRODUCT_DIR)/standalone-community-<(version_string).js', - '<(PRODUCT_DIR)/standalone-community-<(version_string).html', - '<(PRODUCT_DIR)/standalone-community-<(version_string).html.mem', - ], - }, - }, - 'sources!': [ 'src/dummy.cpp', @@ -855,7 +840,7 @@ } ], [ - 'OS == "emscripten"', + 'OS == "emscripten" and target_arch == "js"', { 'targets': [ @@ -871,6 +856,45 @@ 'variables': { 'version_suffix': '<(version_string)', + + 'emscripten_js_libs_pre': + [ + 'src/em-preamble.js', + 'src/em-preamble-overlay.js', + ], + 'emscripten_js_libs': + [ + 'src/em-dc.js', + 'src/em-util.js', + 'src/em-dialog.js', + 'src/em-emterpreter.js', + 'src/em-event.js', + 'src/em-liburl.js', + 'src/em-standalone.js', + 'src/em-surface.js', + 'src/em-system.js', + 'src/em-url.js', + ], + + 'emscripten_javascriptify_output': + [ + '<(PRODUCT_DIR)/standalone-community-<(version_string).js', + '<(PRODUCT_DIR)/standalone-community-<(version_string).html', + '<(PRODUCT_DIR)/standalone-community-<(version_string).html.mem', + ], + }, + + 'all_dependent_settings': + { + 'variables': + { + 'dist_aux_files': + [ + 'rsrc/emscripten-standalone-template/', + 'rsrc/emscripten-startup-template.livecodescript/', + '<@(emscripten_javascriptify_output)' + ], + }, }, 'actions': @@ -917,25 +941,13 @@ '<(PRODUCT_DIR)/standalone-community.bc', 'rsrc/emscripten-html-template.html', '<(PRODUCT_DIR)/standalone-community-whitelist.json', - 'src/em-preamble.js', - 'src/em-preamble-overlay.js', - 'src/em-util.js', - 'src/em-async.js', - 'src/em-dialog.js', - 'src/em-event.js', - 'src/em-surface.js', - 'src/em-system.js', - 'src/em-url.js', - 'src/em-standalone.js', - 'src/em-liburl.js', - 'src/em-dc.js', + '<@(emscripten_js_libs_pre)', + '<@(emscripten_js_libs)', ], 'outputs': [ - '<(PRODUCT_DIR)/standalone-community-<(version_suffix).js', - '<(PRODUCT_DIR)/standalone-community-<(version_suffix).html', - '<(PRODUCT_DIR)/standalone-community-<(version_suffix).html.mem', + '<@(emscripten_javascriptify_output)', ], 'action': @@ -950,19 +962,107 @@ '--whitelist', '<(PRODUCT_DIR)/standalone-community-whitelist.json', '--pre-js', - 'src/em-preamble.js', - 'src/em-preamble-overlay.js', + '<@(emscripten_js_libs_pre)', + '--js-library', + '<@(emscripten_js_libs)', + ], + }, + ], + }, + ], + }, + ], + [ + 'OS == "emscripten" and target_arch == "wasm"', + { + 'targets': + [ + { + 'target_name': 'javascriptify', + 'type': 'none', + + 'dependencies': + [ + 'standalone', + ], + + 'variables': + { + 'version_suffix': '<(version_string)', + + 'emscripten_js_libs_pre': + [ + 'src/em-preamble.js', + 'src/em-preamble-overlay.js', + ], + 'emscripten_js_libs': + [ + 'src/em-asyncify.js', + 'src/em-dc.js', + 'src/em-util.js', + 'src/em-dialog.js', + 'src/em-event.js', + 'src/em-liburl.js', + 'src/em-standalone.js', + 'src/em-surface.js', + 'src/em-system.js', + 'src/em-url.js', + ], + + 'emscripten_javascriptify_output': + [ + '<(PRODUCT_DIR)/standalone-community-<(version_string).js', + '<(PRODUCT_DIR)/standalone-community-<(version_string).html', + '<(PRODUCT_DIR)/standalone-community-<(version_string).wasm', + ], + }, + + 'all_dependent_settings': + { + 'variables': + { + 'dist_aux_files': + [ + 'rsrc/emscripten-standalone-template/', + 'rsrc/emscripten-startup-template.livecodescript/', + '<@(emscripten_javascriptify_output)' + ], + }, + }, + + 'actions': + [ + { + 'action_name': 'javascriptify', + 'message': 'Javascript-ifying the Emscripten engine', + + 'inputs': + [ + '../util/emscripten-javascriptify-wasm.py', + '<(PRODUCT_DIR)/standalone-community.bc', + 'rsrc/emscripten-html-template.html', + '<@(emscripten_js_libs_pre)', + '<@(emscripten_js_libs)', + ], + + 'outputs': + [ + '<@(emscripten_javascriptify_output)', + ], + + 'action': + [ + '../util/emscripten-javascriptify-wasm.py', + '--input', + '<(PRODUCT_DIR)/standalone-community.bc', + '--output', + '<(PRODUCT_DIR)/standalone-community-<(version_suffix).html', + '--shell-file', + 'rsrc/emscripten-html-template.html', + '--pre-js', + '<@(emscripten_js_libs_pre)', '--js-library', - 'src/em-util.js', - 'src/em-async.js', - 'src/em-dialog.js', - 'src/em-event.js', - 'src/em-surface.js', - 'src/em-system.js', - 'src/em-url.js', - 'src/em-standalone.js', - 'src/em-liburl.js', - 'src/em-dc.js', + '<@(emscripten_js_libs)', ], }, ], diff --git a/engine/src/em-async.h b/engine/src/em-async.h index 758d19efe22..abde7cbee2f 100644 --- a/engine/src/em-async.h +++ b/engine/src/em-async.h @@ -38,6 +38,13 @@ extern "C" { */ int MCEmscriptenAsyncYield(real64_t p_timeout_s = -1); +/* Synchronously run callbacks registered by async JS to be run + * once the engine resumes + * + * Defined in em-async.js + */ +void MCEmscriptenAsyncRunHooks(); + /* Continue running the engine's main loop on receipt of an event. * * Returns when the engine next yields. diff --git a/engine/src/em-asyncify.js b/engine/src/em-asyncify.js new file mode 100644 index 00000000000..bd19f1e674b --- /dev/null +++ b/engine/src/em-asyncify.js @@ -0,0 +1,226 @@ +/* -*-Javascript-*- + +Copyright (C) 2015 LiveCode Ltd. + +This file is part of LiveCode. + +LiveCode is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License v3 as published by the Free +Software Foundation. + +LiveCode is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with LiveCode. If not see . */ + +mergeInto(LibraryManager.library, { + + // This class is used to emulate a synchronous LiveCode main loop + // using emscripten's emterpreter + $LiveCodeAsync__deps: ['$Asyncify'], + $LiveCodeAsync: { + // true if ensureInit() has ever been run + _initialised: false, + + // A function that's called to resume the LiveCode main + // loop. + _continuation: null, + + // List of hooks to be run before every resume + _hooks: [], + + // List of callbacks to be run just before resuming the main + // loop. + _preResume: [], + + // The handle for the setTimeout() handler for the current + // yield state. + _timeoutHandle: null, + + // True if the last resume was from a timeout + _fromTimeout: false, + + _ensureInit: function() { + // Make sure this only ever gets run once. + if (LiveCodeAsync._initialised) { + return; + } + LiveCodeAsync._initialised = true; + + LiveCodeAsync.addResumeHook(function () { + // Run pre-resume callbacks + LiveCodeAsync._preResume.forEach(function (c) { c(); }); + // Reset pre-resume callback list + LiveCodeAsync._preResume = []; + }); + }, + + _resumeTimeout: function() { + LiveCodeAsync._fromTimeout = true; + LiveCodeAsync.resume(); + }, + + // Resume the main loop + resume: function(delayed) { + LiveCodeAsync._ensureInit(); + + // For convenience, simplify delay+resume + if (delayed) { + LiveCodeAsync.delay(delayed); + } + + // Don't allow calls to resume() when not actually paused. + if (!LiveCodeAsync.isPaused()) { + return; + } + + // Cancel the timeout for this yield state + if (LiveCodeAsync._timeoutHandle) { + clearTimeout(LiveCodeAsync._timeoutHandle); + } + + var resume = LiveCodeAsync._continuation; + + // Clear state + LiveCodeAsync._timeoutHandle = null; + LiveCodeAsync._continuation = null; + + // Call the resume function, passing the return value + // which must be a number. + resume(LiveCodeAsync.isTimedOut() ? 0 : 1); + }, + + // Yield the main loop; save the execution state and return to + // the browser. The main loop will be resumed the next time + // the browser sends the engine an event. + // + // If no event has occurred by the time milliseconds + // elapse, pause() returns false. Otherwise, + // pause() returns true. + pause: function(timeout) { + LiveCodeAsync._ensureInit(); + + // Suspend execution. Note that pause() might be called + // multiple times for a single yield from the engine, but + // the closure passed to Asyncify.handleSleep() will + // only be called once. This means that all the work + // needs to be done in the closure. + return Asyncify.handleSleep(function(resume) { + + // Can't yield recursively + assert(!LiveCodeAsync._continuation); + assert(!LiveCodeAsync._timeoutHandle); + assert(!LiveCodeAsync._inPreResume); + + LiveCodeAsync._continuation = resume; + + // Make sure that we get restarted in time; if the + // timout is negative, never timeout + LiveCodeAsync._fromTimeout = false; + if (timeout >= 0) { + var event = setTimeout(LiveCodeAsync._resumeTimeout, + timeout); + LiveCodeAsync._timeoutHandle = event; + } + }); + }, + + // Test whether the engine is currently paused + isPaused: function() { + return (!!LiveCodeAsync._continuation); + }, + + // Test whether the engine is currently being resumed from a + // timeout. Returns false if the resume is due to an event. + isTimedOut: function() { + LiveCodeAsync._ensureInit() + return LiveCodeAsync._fromTimeout + }, + + // Add a closure to be run before the engine next resumes + delay: function(delayed) { + LiveCodeAsync._ensureInit(); + + if (!LiveCodeAsync.isPaused()) { + // Engine is live, just run the closure + delayed(); + } else { + LiveCodeAsync._preResume.push(delayed); + } + }, + + // Run pre-resume hooks. + _runHooks: function() { + LiveCodeAsync._hooks.forEach(function (h) { h(); }); + }, + + // Register a closure to be run before every resume. If + // is already in the list of hooks, does nothing. + // + // callback: closure taking no arguments. + addResumeHook: function(callback) { + LiveCodeAsync._ensureInit(); + + // Make sure the same hook doesn't get registered twice + if (LiveCodeAsync._hooks.some(function (h) { + return (h === callback); + })) { + return; + } + + LiveCodeAsync._hooks.push(callback); + }, + + // Remove a closure from the list of pre-resume hooks. If + // is not in the list of hooks, does nothing. + removeResumeHook: function(callback) { + LiveCodeAsync._ensureInit(); + + // Find and remove the specified hook + var numHooks = LiveCodeAsync._hooks.length; + for (var i = 0; i < numHooks; i++) { + if (LiveCodeAsync._hooks[i] === callback) { + LiveCodeAsync._hooks.splice(i, 1); + } + } + }, + }, + + // Yield for up to seconds + MCEmscriptenAsyncYield__deps: ['$LiveCodeAsync'], + MCEmscriptenAsyncYield: function(timeout) { + if (!isFinite(timeout)) { + timeout = -1; + } + return LiveCodeAsync.pause(timeout*1000); + }, + + // Run hooks once engine has resumed + MCEmscriptenAsyncRunHooks__deps: ['$LiveCodeAsync'], + MCEmscriptenAsyncRunHooks: function() { + // Run pre-resume hooks + LiveCodeAsync._runHooks(); + }, + + // Resume the engine on event + MCEmscriptenAsyncResume__deps: ['$LiveCodeAsync'], + MCEmscriptenAsyncResume: function() { + LiveCodeAsync.resume(); + }, + + // Delay a closure until the next time the engine resumes + MCEmscriptenAsyncDelay__deps: ['$LiveCodeAsync'], + MCEmscriptenAsyncDelay: function(delayed) { + LiveCodeAsync.delay(delayed); + }, +}); + +/* + * Local Variables: + * tab-width: 4 + * indent-tabs-mode: t + * End: + */ diff --git a/engine/src/em-dc.cpp b/engine/src/em-dc.cpp index 8d45a99602c..8b381123185 100644 --- a/engine/src/em-dc.cpp +++ b/engine/src/em-dc.cpp @@ -278,12 +278,14 @@ MCScreenDC::wait(real64_t p_duration, Boolean p_allow_dispatch, Boolean p_accept_any_event) { +#if defined(__asmjs__) /* Don't permit inner main loops. They cause amusing "-12" assertion * failures from Emterpreter. */ if (0 < int(MCwaitdepth)) { return true; } +#endif p_duration = MCMax(p_duration, 0.0); @@ -327,6 +329,10 @@ MCScreenDC::wait(real64_t p_duration, if (!t_done && MCEmscriptenAsyncYield(t_sleep_time)) { +#if !defined(__asmjs__) + /* Run any callbacks while waiting to resume */ + MCEmscriptenAsyncRunHooks(); +#endif t_done = p_accept_any_event; } @@ -443,6 +449,12 @@ MCScreenDC::popupaskdialog(uint32_t p_type, MCStringRef p_title, MCStringRef p_m } +/* ================================================================ + * Mouse state + * ================================================================ */ + +Boolean tripleclick = False; + void MCScreenDC::update_mouse_press_state(MCMousePressState p_state, int32_t p_button) { diff --git a/engine/src/em-async.js b/engine/src/em-emterpreter.js similarity index 100% rename from engine/src/em-async.js rename to engine/src/em-emterpreter.js diff --git a/engine/src/em-filehandle.cpp b/engine/src/em-filehandle.cpp index d0dd2cf64f0..bc553641cad 100644 --- a/engine/src/em-filehandle.cpp +++ b/engine/src/em-filehandle.cpp @@ -225,7 +225,7 @@ MCEmscriptenFileHandle::Seek(int64_t p_offset, errno = 0; t_result = lseek(m_fd, p_offset, t_sys_whence); - if (reinterpret_cast(-1) == t_result) + if ((-1) == t_result) { return false; } diff --git a/ide-support/revdeploylibraryemscripten.livecodescript b/ide-support/revdeploylibraryemscripten.livecodescript index ed252f79839..b143395db45 100644 --- a/ide-support/revdeploylibraryemscripten.livecodescript +++ b/ide-support/revdeploylibraryemscripten.livecodescript @@ -89,6 +89,13 @@ command deployDo pTargetStack, pBrowser local tSettings put revSBGetSettings(pTargetStack, true) into tSettings + -- use either js or wasm output - prefer wasm output if enabled in settings + if tSettings["Emscripten","wasm"] then + put false into tSettings["Emscripten","js"] + else + put true into tSettings["Emscripten","js"] + end if + put sHTTPFileSpace & slash & tSettings["name"] into tBuildFolder if there is a folder tBuildFolder then revDeleteFolder tBuildFolder @@ -295,8 +302,22 @@ on deployEmscriptenNewHTTPRequest pSocketID, pRequest set the itemDel to comma put sHTTPFileSpace & slash before tPath + local tMimeType + if tPath ends with ".html" then + put "text/html" into tMimeType + else if tPath ends with ".js" then + put "text/javascript" into tMimeType + else if tPath ends with ".wasm" then + put "application/wasm" into tMimeType + end if + + local tResponseHeaders + if tMimeType is not empty then + put "Content-Type:" && tMimeType & return after tResponseHeaders + end if + if there is a file tPath then - httpdResponse pSocketID, 200, URL ("binfile:" & tPath) + httpdResponse pSocketID, 200, URL ("binfile:" & tPath), tResponseHeaders else set the itemDel to slash if the last item pRequest["resource"] is "stdio.html" and pRequest["method"] is "POST" then diff --git a/ide-support/revsaveasemscriptenstandalone.livecodescript b/ide-support/revsaveasemscriptenstandalone.livecodescript index 5a723d13c72..2b9d56a3cf0 100644 --- a/ide-support/revsaveasemscriptenstandalone.livecodescript +++ b/ide-support/revsaveasemscriptenstandalone.livecodescript @@ -33,6 +33,17 @@ command revSaveAsEmscriptenStandalone pStack, pOutputFolder, pSettings -- FIXME --dispatch "revIDEDeployEmscriptenInitialize" to stack "revDeployLibrary" + local tArchs + repeat for each item tItem in revSBPlatformArchitectures("Emscripten") + if pSettings["Emscripten",tItem] then + put true into tArchs[tItem] + end if + end repeat + if the number of elements of tArchs is 0 then + put true into tArchs["js"] + end if + combine tArchs by comma as set + ---------- Pre-compute standalone build parameters -- Initial stack file @@ -77,83 +88,95 @@ command revSaveAsEmscriptenStandalone pStack, pOutputFolder, pSettings throw "license does not permit HTML5 deployment" end if - ---------- Create a working directory - local tBuildFolder - put pOutputFolder into tBuildFolder - - logDebug "top", "Output folder: " && pOutputFolder - - ---------- Create new zip archive - revStandaloneProgress "Creating standalone archive..." - - local tBuildZip - put tBuildFolder & slash & "standalone.zip" into tBuildZip - - logDebug "top", "Archive:" && tBuildZip - - revZipOpenArchive tBuildZip, "write" - - if the result is not empty then - throw "could not create standalone archive" - end if - - -- Store template files - local tZipTemplateFile, tZipTemplateFolder, tZipTemplatePath - put getStandaloneTemplateFolder() into tZipTemplateFolder - - repeat for each element tZipTemplateFile in getStandaloneTemplateFiles() - put tZipTemplateFolder & slash & tZipTemplateFile into tZipTemplatePath - logDebug "template", "Storing" && tZipTemplatePath - revZipAddUncompressedItemWithFile tBuildZip, tZipTemplateFile, tZipTemplatePath + repeat for each item tArch in tArchs + ---------- Create a working directory + local tBuildFolder + if the number of items in tArchs > 1 then + put pOutputFolder & slash & tArch into tBuildFolder + else + put pOutputFolder into tBuildFolder + end if + + if there is not a folder tBuildFolder then + create folder tBuildFolder + if the result is not empty then + throw "could not create build folder for" && tArch && "target" + end if + end if + + logDebug "top", "Output folder: " && pOutputFolder + + ---------- Create new zip archive + revStandaloneProgress "Creating standalone archive..." + + local tBuildZip + put tBuildFolder & slash & "standalone.zip" into tBuildZip + + logDebug "top", "Archive:" && tBuildZip + + revZipOpenArchive tBuildZip, "write" + if the result is not empty then - throw "could not store template files in standalone archive" + throw "could not create standalone archive" end if - end repeat - - -- Define some paths within the zip archive - local tBootPath, tStandalonePath - local tDeployedStackPath - put "boot" into tBootPath - put tBootPath & slash & "__startup.data" into tDeployedStackPath - put tBootPath & slash & "standalone" into tStandalonePath - - -- Deploy the standalone application data - storeDeployedStack tBuildZip, tDeployedStackPath, tBuildFolder, tStackFile, tDeploy - - -- Store extra, copied files - storeAssets tBuildZip, tResolvedFileData, tStandalonePath, tBaseFolder - - -- Close zip file - revZipCloseArchive tBuildZip - if the result is not empty then - throw "could not create standalone archive:" && the result - end if - - ---------- Copy the engine into the build folder - revStandaloneProgress "Preparing engine files..." - local tEngineFile - repeat for each element tEngineFile in getEngineFiles() - logDebug "engine", "Copying" && tEngineFile - revSBCopyFileToFolder tEngineFile, tBuildFolder, \ - "revStandaloneProgressCallback", the long id of me + -- Store template files + local tZipTemplateFile, tZipTemplateFolder, tZipTemplatePath + put getStandaloneTemplateFolder(tArch) into tZipTemplateFolder + + repeat for each element tZipTemplateFile in getStandaloneTemplateFiles(tArch) + put tZipTemplateFolder & slash & tZipTemplateFile into tZipTemplatePath + logDebug "template", "Storing" && tZipTemplatePath + revZipAddUncompressedItemWithFile tBuildZip, tZipTemplateFile, tZipTemplatePath + if the result is not empty then + throw "could not store template files in standalone archive" + end if + end repeat + + -- Define some paths within the zip archive + local tBootPath, tStandalonePath + local tDeployedStackPath + put "boot" into tBootPath + put tBootPath & slash & "__startup.data" into tDeployedStackPath + put tBootPath & slash & "standalone" into tStandalonePath + + -- Deploy the standalone application data + storeDeployedStack tArch, tBuildZip, tDeployedStackPath, tBuildFolder, tStackFile, tDeploy + + -- Store extra, copied files + storeAssets tBuildZip, tResolvedFileData, tStandalonePath, tBaseFolder + + -- Close zip file + revZipCloseArchive tBuildZip if the result is not empty then - throw "could not copy engine file" && the result + throw "could not create standalone archive:" && the result end if + + ---------- Copy the engine into the build folder + revStandaloneProgress "Preparing engine files..." + local tEngineFile + repeat for each element tEngineFile in getEngineFiles(tArch) + logDebug "engine", "Copying" && tEngineFile + revSBCopyFileToFolder tEngineFile, tBuildFolder, \ + "revStandaloneProgressCallback", the long id of me + + if the result is not empty then + throw "could not copy engine file" && the result + end if + end repeat + + ---------- Create HTML page + + -- FIXME just copies the Emscripten-generated page into place + revStandaloneProgress "Preparing HTML page..." + + local tHtmlFile + put tBuildFolder & slash & tName & ".html" into tHtmlFile + logDebug "html", "Creating" && tHtmlFile + logDebug "html", "Copying" && getHtmlTemplateFile(tArch) + revSBCopyFileToFile mapBinPath(tArch, getHtmlTemplateFile(tArch)), tHtmlFile, \ + "revStandaloneProgressCallback", the long id of me end repeat - - ---------- Create HTML page - - -- FIXME just copies the Emscripten-generated page into place - revStandaloneProgress "Preparing HTML page..." - - local tHtmlFile - put tBuildFolder & slash & tName & ".html" into tHtmlFile - logDebug "html", "Creating" && tHtmlFile - logDebug "html", "Copying" && getHtmlTemplateFile() - revSBCopyFileToFile mapBinPath(getHtmlTemplateFile()), tHtmlFile, \ - "revStandaloneProgressCallback", the long id of me - catch tError if tError is empty then put "an unknown error occurred" into tError @@ -184,7 +207,7 @@ end revSaveAsEmscriptenStandalone -- Standalone deployment ---------------------------------------------------------------- -private command storeDeployedStack pZip, pDeployPath, pBuildFolder, pMainStack, pDeployParams +private command storeDeployedStack pArch, pZip, pDeployPath, pBuildFolder, pMainStack, pDeployParams local tTempStackData, tTempStackPath, tStartupScript, tTempDeployPath ---------- Generate the startup script @@ -194,7 +217,7 @@ private command storeDeployedStack pZip, pDeployPath, pBuildFolder, pMainStack, put getPreparedStackAsData(pMainStack, pBuildFolder) into tTempStackData put tTempStackData into url ("binfile:" & tTempStackPath) - put getStartupScript(pDeployParams["startup_script"]) into tStartupScript + put getStartupScript(pArch, pDeployParams["startup_script"]) into tStartupScript put pBuildFolder & slash & "emscriptendeploy.data" into tTempDeployPath @@ -241,11 +264,11 @@ end storeDeployedStack -- Create the startup script by processing the startup stack template. -- Engine-specific and standalone-specific data is substituted into the -- template in order -private function getStartupScript pGeneratedStartupScript +private function getStartupScript pArch, pGeneratedStartupScript local tTemplateFile, tScript -- Load template - put getStartupTemplateFile() into tTemplateFile + put getStartupTemplateFile(pArch) into tTemplateFile open file tTemplateFile for "UTF-8" text read if the result is not empty then throw tTemplateFile & ":" && the result @@ -397,31 +420,31 @@ end getPreparedStackAsData -- Map a compiled file's path into an absolute filesystem path. This -- is used to cope with running from a git repository working tree. -private function mapBinPath pPath +private function mapBinPath pArch, pPath if there is a file pPath then return pPath end if if revEnvironmentIsInstalled() then -- We're running from an installed version of LiveCode - return getRuntimeFolder() & slash & pPath + return getRuntimeFolder(pArch) & slash & pPath else -- We're running from a git checkout - return getRepoBinariesFolder() & slash & pPath + return getRepoBinariesFolder(pArch) & slash & pPath end if end mapBinPath -- Map a resource file's path into an absolute filesystem path. This -- is used to cope with running from a git repository working tree. -private function mapResPath pPath +private function mapResPath pArch, pPath if there is a file pPath then return pPath end if if revEnvironmentIsInstalled() then - return getRuntimeFolder() & slash & pPath + return getRuntimeFolder(pArch) & slash & pPath else - return getRepoResourceFolder() & slash & pPath + return getRepoResourceFolder(pArch) & slash & pPath end if end mapResPath @@ -489,20 +512,28 @@ private command scanFolder_recurse @xCount, @xFiles, pRelPath set the defaultFolder to tSaveFolder end scanFolder_recurse -function revGetEmscriptenFiles - return getEngineFiles() +function revGetEmscriptenFiles pArch + return getEngineFiles(pArch) end revGetEmscriptenFiles -- Return a number-indexed array of engine files that need to be -- copied into the standalone output folder. -private function getEngineFiles +private function getEngineFiles pArch local tFiles -- Engine JavaScript - put mapBinPath("standalone-" & getEngineType() & "-" & the version & ".js") into tFiles[1] - - -- Memory initialisation file - put mapBinPath("standalone-" & getEngineType() & "-" & the version & ".html.mem") into tFiles[2] + put mapBinPath(pArch, "standalone-" & getEngineType() & "-" & the version & ".js") into tFiles[1] + + switch pArch + case "js" + -- Memory initialisation file + put mapBinPath(pArch, "standalone-" & getEngineType() & "-" & the version & ".html.mem") into tFiles[2] + break + case "wasm" + -- Engine WebAssembly binary + put mapBinPath(pArch, "standalone-" & getEngineType() & "-" & the version & ".wasm") into tFiles[2] + break + end switch return tFiles end getEngineFiles @@ -511,36 +542,36 @@ end getEngineFiles -- This is a folder that contains the default filesystem -- initialisation data that's automatically included in all -- standalones (especially fonts). -private function getStandaloneTemplateFolder - return mapResPath("emscripten-standalone-template") +private function getStandaloneTemplateFolder pArch + return mapResPath(pArch, "emscripten-standalone-template") end getStandaloneTemplateFolder -- Return a number-indexed array of files in the Emscripten standalone -- template folder. The paths returned are relative to the path returned -- by getStandaloneTemplateFolder() -private function getStandaloneTemplateFiles - return scanFolder(getStandaloneTemplateFolder()) +private function getStandaloneTemplateFiles pArch + return scanFolder(getStandaloneTemplateFolder(pArch)) end getStandaloneTemplateFiles -- Get the filename of the Emscripten standalone template HTML file. -- This has some standalone-dependent JavaScript substituted into it -- to instantiate the engine. -private function getHtmlTemplateFile - return mapBinPath("standalone-" & getEngineType() & "-" & the version & ".html") +private function getHtmlTemplateFile pArch + return mapBinPath(pArch, "standalone-" & getEngineType() & "-" & the version & ".html") end getHtmlTemplateFile -- Get the filename of the Emscripten standalone startup script-only -- stack template. -private function getStartupTemplateFile - return mapResPath("emscripten-startup-template.livecodescript") +private function getStartupTemplateFile pArch + return mapResPath(pArch, "emscripten-startup-template.livecodescript") end getStartupTemplateFile -- Get the directory containing the Emscripten runtime -private function getRuntimeFolder +private function getRuntimeFolder pArch local tRelFolder, tOverridePath, tUserPath - put slash & "Emscripten" & slash & "js" into tRelFolder + put slash & "Emscripten" & slash & pArch into tRelFolder put revOverrideRuntimePath() into tOverridePath if tOverridePath is not empty then @@ -563,8 +594,8 @@ end getRuntimeFolder -- Get the directory where Emscripten binaries end up (specifically, -- the engine's JavaScript file). -private function getRepoBinariesFolder - return revEnvironmentRepositoryPath() & slash & "emscripten-bin" +private function getRepoBinariesFolder pArch + return revEnvironmentRepositoryPath() & slash & "emscripten-" & pArch & "-bin" end getRepoBinariesFolder -- Get the directory where Emscripten resources are kept diff --git a/ide-support/revsblibrary.livecodescript b/ide-support/revsblibrary.livecodescript index 8763dcba114..e8161c862aa 100644 --- a/ide-support/revsblibrary.livecodescript +++ b/ide-support/revsblibrary.livecodescript @@ -742,7 +742,7 @@ constant kMacOSXArchitectures = "x86-32,x86-64" constant kLinuxArchitectures = "x86-32,x86-64,armv6-hf" constant kAndroidArchitectures = "armv7,arm64,x86,x86_64" constant kiOSArchitectures = "armv7,arm64" -constant kEmscriptenArchitectures = "js" +constant kEmscriptenArchitectures = "js,wasm" function revSBPlatformArchitectures pPlatform switch pPlatform @@ -1050,11 +1050,16 @@ function revEngineCheck pEngine return true end if break - case "Emscripten" + case "Emscripten js" if there is a folder (tPath & "/Emscripten/js") then return true end if break + case "Emscripten wasm" + if there is a folder (tPath & "/Emscripten/wasm") then + return true; + end if + break end switch end repeat return false diff --git a/libbrowser/libbrowser.gyp b/libbrowser/libbrowser.gyp index d995c432b2d..1427604be79 100644 --- a/libbrowser/libbrowser.gyp +++ b/libbrowser/libbrowser.gyp @@ -57,6 +57,7 @@ 'src/libbrowser_win_factories.cpp', 'src/libbrowser_osx_factories.cpp', 'src/libbrowser_ios_factories.cpp', + 'src/libbrowser_emscripten_factories.cpp', ], 'target_conditions': @@ -157,6 +158,16 @@ ], }, ], + + [ + 'toolset_os != "emscripten"', + { + 'sources!': + [ + 'src/libbrowser_emscripten_factories', + ], + }, + ], ], 'link_settings': diff --git a/libbrowser/src/libbrowser_emscripten_factories.cpp b/libbrowser/src/libbrowser_emscripten_factories.cpp new file mode 100644 index 00000000000..7f9dd5ffbb1 --- /dev/null +++ b/libbrowser/src/libbrowser_emscripten_factories.cpp @@ -0,0 +1,25 @@ +/* Copyright (C) 2020 LiveCode Ltd. + + This file is part of LiveCode. + + LiveCode is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License v3 as published by the Free + Software Foundation. + + LiveCode is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + You should have received a copy of the GNU General Public License + along with LiveCode. If not see . */ + +#include + +#include "libbrowser_internal.h" + +//////////////////////////////////////////////////////////////////////////////// + +MCBrowserFactoryMap* s_factory_list = nil; + +//////////////////////////////////////////////////////////////////////////////// diff --git a/prebuilt/fetch-libraries.sh b/prebuilt/fetch-libraries.sh index 93cc4a3d1a7..29a1db695b7 100755 --- a/prebuilt/fetch-libraries.sh +++ b/prebuilt/fetch-libraries.sh @@ -7,7 +7,7 @@ ARCHS_mac=( Universal ) ARCHS_ios=( Universal ) ARCHS_win32=( x86 x86_64 ) ARCHS_linux=( i386 x86_64 ) -ARCHS_emscripten=( js ) +ARCHS_emscripten=( js wasm ) LIBS_android=( Thirdparty OpenSSL ICU ) LIBS_mac=( Thirdparty OpenSSL ICU ) LIBS_ios=( Thirdparty OpenSSL ICU ) diff --git a/prebuilt/fetch.gyp b/prebuilt/fetch.gyp index ca984c6d6e6..ca70ad90ef7 100644 --- a/prebuilt/fetch.gyp +++ b/prebuilt/fetch.gyp @@ -477,13 +477,14 @@ 'outputs': [ - 'lib/emscripten/js', + 'lib/emscripten/>(target_arch)', ], 'action': [ './fetch-libraries.sh', 'emscripten', + '>(target_arch)' ], }, ], diff --git a/prebuilt/libicu.gyp b/prebuilt/libicu.gyp index 2b52c648298..d7896c937bb 100755 --- a/prebuilt/libicu.gyp +++ b/prebuilt/libicu.gyp @@ -238,11 +238,11 @@ }, ], [ - 'OS == "emscripten"', + 'toolset_os == "emscripten"', { 'library_dirs': [ - 'lib/emscripten/js', + 'lib/emscripten/>(toolset_arch)', ], 'libraries': diff --git a/prebuilt/scripts/build-thirdparty.sh b/prebuilt/scripts/build-thirdparty.sh index e53edef1646..3e82ff98c70 100755 --- a/prebuilt/scripts/build-thirdparty.sh +++ b/prebuilt/scripts/build-thirdparty.sh @@ -43,10 +43,9 @@ elif [ "$PLATFORM" == "ios" ]; then BUILDPATH="../_build/ios/$SUBPLATFORM/Release" LIBPATH="lib/ios/$SUBPLATFORM" elif [ "$PLATFORM" == "emscripten" ]; then - MAKE_TARGET=emscripten LIBS="${Thirdparty_LIBS_emscripten}" - BUILDPATH="../build-emscripten/livecode/out/Release/obj.target/thirdparty" - LIBPATH="lib/emscripten/js" + BUILDPATH="../build-emscripten-$ARCH/livecode/out/Release/obj.target/thirdparty" + LIBPATH="lib/emscripten/$ARCH" elif [ "$PLATFORM" == "android" ]; then LIBS="${Thirdparty_LIBS_android}" BUILDPATH="../build-android-$ARCH/livecode/out/Release/obj.target/thirdparty" @@ -66,7 +65,7 @@ elif [ "$PLATFORM" == "linux" ] ; then make -C "../build-${PLATFORM}-${ARCH}/livecode" thirdparty-prebuilts elif [ "$PLATFORM" == "emscripten" ] ; then export BUILDTYPE=Release - ${EMMAKE} make -j16 -C "../build-${PLATFORM}/livecode" thirdparty-prebuilts + ${EMMAKE} make -j16 -C "../build-${PLATFORM}-${ARCH}/livecode" thirdparty-prebuilts elif [ "$PLATFORM" == "android" ] ; then export BUILDTYPE=Release make -j16 -C "../build-${PLATFORM}-${ARCH}/livecode" thirdparty-prebuilts diff --git a/prebuilt/thirdparty.gyp b/prebuilt/thirdparty.gyp index 0803d2b3173..436894b036e 100644 --- a/prebuilt/thirdparty.gyp +++ b/prebuilt/thirdparty.gyp @@ -76,11 +76,11 @@ }, ], [ - 'OS == "emscripten"', + 'toolset_os == "emscripten"', { 'library_dirs': [ - 'lib/emscripten/js', + 'lib/emscripten/>(toolset_arch)', ], }, ], @@ -231,7 +231,7 @@ }, ], [ - 'OS == "emscripten"', + 'toolset_os == "emscripten"', { 'libraries': [ @@ -333,7 +333,7 @@ }, ], [ - 'OS == "emscripten"', + 'toolset_os == "emscripten"', { 'libraries': [ @@ -435,7 +435,7 @@ }, ], [ - 'OS == "emscripten"', + 'toolset_os == "emscripten"', { 'libraries': [ @@ -537,7 +537,7 @@ }, ], [ - 'OS == "emscripten"', + 'toolset_os == "emscripten"', { 'libraries': [ @@ -594,6 +594,13 @@ # Disable Skia debugging 'SK_RELEASE', + # Don't try to link against libetc1 for ETC1 texture compression support + 'SK_IGNORE_ETC1_SUPPORT', + + # Some Skia source files need this to build + # TODO: see if those files can be removed from the build + 'SK_SUPPORT_LEGACY_IMAGE_ENCODER_CLASS', + # We use deprecated Skia features 'SK_SUPPORT_LEGACY_CANVAS_IS_REFCNT', 'SK_SUPPORT_LEGACY_GETTOPDEVICE', @@ -654,7 +661,6 @@ { 'libraries': [ - 'lib/ios/$(SDK_NAME)/libskia.a', 'lib/ios/$(SDK_NAME)/libskia.a', 'lib/ios/$(SDK_NAME)/libskia_opt_none.a', 'lib/ios/$(SDK_NAME)/libskia_opt_arm.a', @@ -727,7 +733,7 @@ }, ], [ - 'OS == "emscripten"', + 'toolset_os == "emscripten"', { 'libraries': [ @@ -789,7 +795,7 @@ }, ], [ - 'OS == "emscripten"', + 'toolset_os == "emscripten"', { 'libraries': [ @@ -850,7 +856,7 @@ }, ], [ - 'OS == "emscripten"', + 'toolset_os == "emscripten"', { 'libraries': [ @@ -1098,7 +1104,7 @@ }, ], [ - 'OS == "emscripten"', + 'toolset_os == "emscripten"', { 'libraries': [ @@ -1200,7 +1206,7 @@ }, ], [ - 'OS == "emscripten"', + 'toolset_os == "emscripten"', { 'libraries': [ @@ -1302,7 +1308,7 @@ }, ], [ - 'OS == "emscripten"', + 'toolset_os == "emscripten"', { 'libraries': [ @@ -1575,7 +1581,7 @@ }, ], [ - 'OS == "emscripten"', + 'toolset_os == "emscripten"', { 'libraries': [ @@ -1677,7 +1683,7 @@ }, ], [ - 'OS == "emscripten"', + 'toolset_os == "emscripten"', { 'libraries': [ @@ -2005,7 +2011,7 @@ }, ], [ - 'OS == "emscripten"', + 'toolset_os == "emscripten"', { 'libraries': [ diff --git a/util/emscripten-javascriptify-wasm.py b/util/emscripten-javascriptify-wasm.py new file mode 100755 index 00000000000..b449ce8ffeb --- /dev/null +++ b/util/emscripten-javascriptify-wasm.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python + +# This script runs the command that converts the LLVM bitcode files +# produced by the emscripten compiler to JavaScript. + +import sys +import os +import subprocess + +# Compiler settings +# ----------------- +# +# These should generally match the settings in config/emscripten-settings.gypi +settings = { + 'ASYNCIFY': 1, + 'ASYNCIFY_IMPORTS': '[MCEmscriptenAsyncYield]', + 'ALLOW_MEMORY_GROWTH': 1, + 'DEMANGLE_SUPPORT': 1, + 'ERROR_ON_UNDEFINED_SYMBOLS': 0, + 'EXTRA_EXPORTED_RUNTIME_METHODS': '[addRunDependency,removeRunDependency,ccall]', + 'RESERVED_FUNCTION_POINTERS': 1024, + 'TOTAL_MEMORY': 67108864, + 'USE_SDL': 0, + 'WARN_ON_UNDEFINED_SYMBOLS': 1, + } + +# Get the build type and the compiler command +# ------------------------------------------- + +env_verbose = os.getenv('V', '0') +env_emcc = os.getenv('EMCC', 'emcc') +env_build_type = os.getenv('BUILDTYPE', 'Debug') +env_cflags = os.getenv('CFLAGS', '') + +if env_build_type == 'Release': + optimisation_flags = ['-O2', '-g0'] +else: + optimisation_flags = ['-O2', '-g3'] + +# Separate out separate elements of command line +emcc = env_emcc.split() +cflags = env_cflags.split() + +# Process command line options +# ---------------------------- +# +# Each option absorbs all subsequent arguments up to the next option. +# Options are identified by the fact they start with "--". + +option = None +options = {} +for arg in sys.argv[1:]: + if arg.startswith('--'): + option = arg[2:] + options[option] = [] + else: + if option is None: + print('ERROR: unrecognized option \'{}\''.format(arg)) + sys.exit(1) + options[option].append(arg) + +# Construct emcc command line +# --------------------------- + +command = emcc + ["--emrun"] + optimisation_flags + cflags + +for input in options['input']: + command.append(input) + +command += ['-o', options['output'][0]] + +for setting in sorted(settings.keys()): + command += ['-s', '{}={}'.format(setting, settings[setting])] + +for option in ['pre-js', 'shell-file', 'js-library']: + if options.has_key(option): + for value in options[option]: + command += ['--' + option, value] + +# Run emcc +# -------- + +if env_verbose.strip() is not '0': + print(" ".join(command)) + +emcc_result = subprocess.call(command) +sys.exit(emcc_result) diff --git a/util/emscripten-javascriptify.py b/util/emscripten-javascriptify.py index 9abcaa195f2..859e52f6ce2 100755 --- a/util/emscripten-javascriptify.py +++ b/util/emscripten-javascriptify.py @@ -17,7 +17,7 @@ 'DEMANGLE_SUPPORT': 1, 'EMTERPRETIFY': 1, 'EMTERPRETIFY_ASYNC': 1, - 'LINKABLE': 1, + 'ERROR_ON_UNDEFINED_SYMBOLS': 0, 'RESERVED_FUNCTION_POINTERS': 1024, 'TOTAL_MEMORY': 67108864, 'WARN_ON_UNDEFINED_SYMBOLS': 1, @@ -29,10 +29,10 @@ env_verbose = os.getenv('V', '0') env_emcc = os.getenv('EMCC', 'emcc') env_build_type = os.getenv('BUILDTYPE', 'Debug') -env_cflags = os.getenv('CFLAGS', []) +env_cflags = os.getenv('CFLAGS', '') if env_build_type == 'Release': - optimisation_flags = ['-Os', '-g0'] + optimisation_flags = ['-O2', '-g0'] else: optimisation_flags = ['-O2', '-g3']