diff --git a/ChangeLog b/ChangeLog index cca1b83311894..94a24675d1ce6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -7,6 +7,44 @@ To browse or download snapshots of old tagged versions, visit https://github.com Not all changes are documented here. In particular, new features, user-oriented fixes, options, command-line parameters, usage changes, deprecations, significant internal modifications and optimizations etc. generally deserve a mention. To examine the full set of changes between versions, visit the link to full changeset diff at the end of each section. +Current trunk code +------------------ + - To see a list of commits that in the active development branch 'incoming', which have not yet been packaged in a release, see + https://github.com/kripken/emscripten/compare/1.7.5...incoming + +v1.7.5: 11/13/2013 +------------------ + - Fix issues with the built-in C++ function name demangler. + - Full list of changes: https://github.com/kripken/emscripten/compare/1.7.4...1.7.5 + +v1.7.4: 11/12/2013 +------------------ + - Fixed issues with BSD sockets code and SDL joystick implementation. + - Full list of changes: https://github.com/kripken/emscripten/compare/1.7.3...1.7.4 + +v1.7.3: 11/12/2013 +------------------ + - Added support for generating single-precision floating point instructions. + - For more information, read https://blog.mozilla.org/javascript/2013/11/07/efficient-float32-arithmetic-in-javascript/ + - Made GLES2 support library more spec-conformant by throwing fewer exceptions on errors. Be sure to build with -s GL_ASSERTIONS=1, remember to use glGetError() and check the browser console to best detect WebGL rendering errors. + - Converted return value of emscripten_get_now() from float to double, to not lose precision in the function call. + - Added support for joysticks in SDL via the Gamepad API + - Full list of changes: https://github.com/kripken/emscripten/compare/1.7.2...1.7.3 + +v1.7.2: 11/9/2013 +------------------ + - The compiler now always generates a .js file that contains the generated source code even when compiling to a .html file. + - Read https://groups.google.com/forum/#!topic/emscripten-discuss/EuHMwqdSsEs + - Implemented depth+stencil buffer choosing behavior in GLUT, SDL and GLFW. + - Fixed memory leaks generated by glGetString and eglGetString. + - Greatly optimized startup times when virtual filesystems with a large amount of files in them. + - Added some support for SIMD generated by LLVM. + - Fixed some mappings with SDL keyboard codes. + - Added a new command line parameter --no-heap-copy to compiler and file packager that can be used to optimize VFS memory usage at startup. + - Updated libcxx to revision 194185, 2013-11-07. + - Improvements to various library support. + - Full list of changes: https://github.com/kripken/emscripten/compare/1.7.1...1.7.2 + v1.7.1: 10/24/2013 ------------------ - Remove old call to Runtime.warn in file packager code diff --git a/cmake/Modules/FindOpenAL.cmake b/cmake/Modules/FindOpenAL.cmake new file mode 100644 index 0000000000000..3170966d66c4c --- /dev/null +++ b/cmake/Modules/FindOpenAL.cmake @@ -0,0 +1,26 @@ +# Locate OpenAL +# This module defines +# OPENAL_LIBRARY +# OPENAL_FOUND, if false, do not try to link to OpenAL +# OPENAL_INCLUDE_DIR, where to find the headers + +# The implementation is based on the standard FindOpenAL.cmake provided with CMake, +# but customized for targeting Emscripten only. + +if (NOT OPENAL_FOUND) + SET(OPENAL_FOUND TRUE) + + # For Emscripten-compiled apps in the test suite (test_alut), this is expected... + SET(OPENAL_INCLUDE_DIR "${EMSCRIPTEN_ROOT_PATH}/system/include") + # ... but the stock FindOpenAL.cmake would have returned this. + #SET(OPENAL_INCLUDE_DIR "${EMSCRIPTEN_ROOT_PATH}/system/include/AL") + + # No library to link against for OpenAL, this is picked up automatically by library_openal.js, + # but need to report something, or CMake thinks we failed in the search. + SET(OPENAL_LIBRARY "nul") + SET(OPENAL_LIB "") + + set(CMAKE_INCLUDE_PATH ${CMAKE_INCLUDE_PATH} "${EMSCRIPTEN_ROOT_PATH}/system/include" "${EMSCRIPTEN_ROOT_PATH}/system/include/AL") + + MARK_AS_ADVANCED(OPENAL_LIBRARY OPENAL_INCLUDE_DIR) +endif() diff --git a/cmake/Platform/Emscripten.cmake b/cmake/Platform/Emscripten.cmake index e1e54ccf78852..c30632cafbde1 100644 --- a/cmake/Platform/Emscripten.cmake +++ b/cmake/Platform/Emscripten.cmake @@ -44,9 +44,10 @@ endif() # Normalize, convert Windows backslashes to forward slashes or CMake will crash. get_filename_component(EMSCRIPTEN_ROOT_PATH "${EMSCRIPTEN_ROOT_PATH}" ABSOLUTE) -if ("${CMAKE_MODULE_PATH}" STREQUAL "") - set(CMAKE_MODULE_PATH "${EMSCRIPTEN_ROOT_PATH}/cmake") +if (NOT CMAKE_MODULE_PATH) + set(CMAKE_MODULE_PATH "") endif() +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${EMSCRIPTEN_ROOT_PATH}/cmake/Modules") set(CMAKE_FIND_ROOT_PATH "${EMSCRIPTEN_ROOT_PATH}/cmake") @@ -82,6 +83,8 @@ set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE BOTH) set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) +set(CMAKE_SYSTEM_INCLUDE_PATH "${EMSCRIPTEN_ROOT_PATH}/system/include") + # We would prefer to specify a standard set of Clang+Emscripten-friendly common convention for suffix files, especially for CMake executable files, # but if these are adjusted, ${CMAKE_ROOT}/Modules/CheckIncludeFile.cmake will fail, since it depends on being able to compile output files with predefined names. #SET(CMAKE_LINK_LIBRARY_SUFFIX "") diff --git a/emcc b/emcc index c3f9d862a324e..6a483a39c85fb 100755 --- a/emcc +++ b/emcc @@ -50,7 +50,7 @@ emcc can be influenced by a few environment variables: import os, sys, shutil, tempfile, subprocess, shlex, time, re, logging from subprocess import PIPE, STDOUT from tools import shared, jsrun -from tools.shared import Compression, execute, suffix, unsuffixed, unsuffixed_basename +from tools.shared import Compression, execute, suffix, unsuffixed, unsuffixed_basename, WINDOWS from tools.response_file import read_response_file CXX_SUFFIXES = ('.cpp', '.cxx', '.cc') @@ -887,8 +887,17 @@ try: check_bad_eq(newargs[i]) parts = newargs[i+1].split(',') assert len(parts) == 3, '--compression requires specifying native_encoder,js_decoder,js_name - see emcc --help. got: %s' % newargs[i+1] - Compression.encoder = parts[0] - Compression.decoder = parts[1] + def locate(tool): + if WINDOWS: + if os.path.exists(tool+'.exe'): + return tool+'.exe' + if os.path.exists(tool+'.bat'): + return tool+'.bat' + if os.path.exists(tool+'.cmd'): + return tool+'.cmd' + return tool + Compression.encoder = locate(parts[0]) + Compression.decoder = locate(parts[1]) Compression.js_name = parts[2] assert os.path.exists(Compression.encoder), 'native encoder %s does not exist' % Compression.encoder assert os.path.exists(Compression.decoder), 'js decoder %s does not exist' % Compression.decoder @@ -1265,7 +1274,16 @@ try: shutil.move(in_temp(unsuffixed(uniquename(input_file)) + '.o'), unsuffixed_basename(input_file) + '.' + final_suffix) else: if len(input_files) == 1: - shutil.move(in_temp(unsuffixed(uniquename(input_files[0])) + '.o'), specified_target) + temp_output_base = in_temp(unsuffixed(uniquename(input_files[0]))) + shutil.move(temp_output_base + '.o', specified_target) + if os.path.exists(temp_output_base + '.d'): + # There was a .d file generated, from -MD or -MMD and friends, save a copy of it to where the output resides, + # adjusting the target name away from the temporary file name to the specified target. + # It will be deleted with the rest of the temporary directory. + deps = open(temp_output_base + '.d').read() + deps = deps.replace(temp_output_base + '.o', specified_target) + with open(os.path.join(os.path.dirname(specified_target), os.path.basename(unsuffixed(input_files[0]) + '.d')), "w") as out_dep: + out_dep.write(deps) else: assert len(original_input_files) == 1 or not has_dash_c, 'fatal error: cannot specify -o with -c with multiple files' + str(sys.argv) + ':' + str(original_input_files) # We have a specified target (-o ), which is not JavaScript or HTML, and diff --git a/src/intertyper.js b/src/intertyper.js index f92a04db82851..940c677fce1ef 100644 --- a/src/intertyper.js +++ b/src/intertyper.js @@ -660,9 +660,10 @@ function intertyper(lines, sidePass, baseLineNums) { var tokensLeft = item.tokens.slice(2); item.ident = eatLLVMIdent(tokensLeft); if (item.ident == 'asm') { + warnOnce('inline JavaScript using asm() has some oddities due to how gcc asm() syntax works. use EM_ASM where possible (see emscripten.h)'); if (ASM_JS) { Types.hasInlineJS = true; - warnOnce('inline JavaScript (asm, EM_ASM) will cause the code to no longer fall in the asm.js subset of JavaScript, which can reduce performance - consider using emscripten_run_script'); + warnOnce('inline JavaScript using asm() will cause the code to no longer fall in the asm.js subset of JavaScript, which can reduce performance - consider using emscripten_run_script'); } assert(TARGET_LE32, 'inline js is only supported in le32'); // Inline assembly is just JavaScript that we paste into the code diff --git a/src/library.js b/src/library.js index 6d821bfc1a492..a5380c3a2e501 100644 --- a/src/library.js +++ b/src/library.js @@ -3193,7 +3193,7 @@ LibraryManager.library = { } } if (!finalBase) finalBase = 10; - start = str; + var start = str; // Get digits. var chr; @@ -6122,7 +6122,7 @@ LibraryManager.library = { clock_gettime: function(clk_id, tp) { // int clock_gettime(clockid_t clk_id, struct timespec *tp); var now; - if (clk_id === {{{ cDefine('CLOCK_REALTIME') }}}) { + if (clk_id === {{{ cDefine('CLOCK_REALTIME') }}}) { now = Date.now(); } else { now = _emscripten_get_now(); @@ -6136,10 +6136,17 @@ LibraryManager.library = { // Nothing. return 0; }, + clock_getres__deps: ['emscripten_get_now_res'], clock_getres: function(clk_id, res) { // int clock_getres(clockid_t clk_id, struct timespec *res); + var nsec; + if (clk_id === {{{ cDefine('CLOCK_REALTIME') }}}) { + nsec = 1000 * 1000; + } else { + nsec = _emscripten_get_now_res(); + } {{{ makeSetValue('res', C_STRUCTS.timespec.tv_sec, '1', 'i32') }}} - {{{ makeSetValue('res', C_STRUCTS.timespec.tv_nsec, '1000 * 1000', 'i32') }}} // resolution is milliseconds + {{{ makeSetValue('res', C_STRUCTS.timespec.tv_nsec, 'nsec', 'i32') }}} // resolution is milliseconds return 0; }, @@ -8230,7 +8237,7 @@ LibraryManager.library = { }, accept__deps: ['$FS', '$SOCKFS', '$DNS', '$ERRNO_CODES', '__setErrNo', '_write_sockaddr'], - accept: function(fd, addrp, addrlen) { + accept: function(fd, addr, addrlen) { var sock = SOCKFS.getSocket(fd); if (!sock) { ___setErrNo(ERRNO_CODES.EBADF); @@ -8238,7 +8245,7 @@ LibraryManager.library = { } try { var newsock = sock.sock_ops.accept(sock); - if (addrp) { + if (addr) { var res = __write_sockaddr(addr, newsock.family, DNS.lookup_name(newsock.daddr), newsock.dport); assert(!res.errno); } @@ -8669,21 +8676,28 @@ LibraryManager.library = { }, emscripten_asm_const: function(code) { - // code is a constant string on the heap, so we can cache these - if (!Runtime.asmConstCache) Runtime.asmConstCache = {}; - var func = Runtime.asmConstCache[code]; - if (func) return func(); - func = Runtime.asmConstCache[code] = eval('(function(){ ' + Pointer_stringify(code) + ' })'); // new Function does not allow upvars in node - return func(); + Runtime.getAsmConst(code, 0)(); + }, + + emscripten_asm_const_int__jsargs: true, + emscripten_asm_const_int: function(code) { + var args = Array.prototype.slice.call(arguments, 1); + return Runtime.getAsmConst(code, args.length).apply(null, args) | 0; + }, + + emscripten_asm_const_double__jsargs: true, + emscripten_asm_const_double: function(code) { + var args = Array.prototype.slice.call(arguments, 1); + return +Runtime.getAsmConst(code, args.length).apply(null, args); }, emscripten_get_now: function() { if (!_emscripten_get_now.actual) { if (ENVIRONMENT_IS_NODE) { - _emscripten_get_now.actual = function _emscripten_get_now_actual() { - var t = process['hrtime'](); - return t[0] * 1e3 + t[1] / 1e6; - } + _emscripten_get_now.actual = function _emscripten_get_now_actual() { + var t = process['hrtime'](); + return t[0] * 1e3 + t[1] / 1e6; + } } else if (typeof dateNow !== 'undefined') { _emscripten_get_now.actual = dateNow; } else if (ENVIRONMENT_IS_WEB && window['performance'] && window['performance']['now']) { @@ -8695,6 +8709,17 @@ LibraryManager.library = { return _emscripten_get_now.actual(); }, + emscripten_get_now_res: function() { // return resolution of get_now, in nanoseconds + if (ENVIRONMENT_IS_NODE) { + return 1; // nanoseconds + } else if (typeof dateNow !== 'undefined' || + (ENVIRONMENT_IS_WEB && window['performance'] && window['performance']['now'])) { + return 1000; // microseconds (1/1000 of a millisecond) + } else { + return 1000*1000; // milliseconds + } + }, + //============================ // emscripten vector ops //============================ diff --git a/src/library_browser.js b/src/library_browser.js index 1883c3be7a847..8444fb73281c4 100644 --- a/src/library_browser.js +++ b/src/library_browser.js @@ -250,7 +250,9 @@ mergeInto(LibraryManager.library, { contextAttributes.preserveDrawingBuffer = true; #endif - ctx = canvas.getContext('experimental-webgl', contextAttributes); + ['experimental-webgl', 'webgl'].some(function(webglId) { + return ctx = canvas.getContext(webglId, contextAttributes); + }); } else { ctx = canvas.getContext('2d'); } diff --git a/src/library_sdl.js b/src/library_sdl.js index f780e15a134ea..eb8eea9775d90 100644 --- a/src/library_sdl.js +++ b/src/library_sdl.js @@ -2675,7 +2675,7 @@ var LibrarySDL = { SDL_WaitThread: function() { throw 'SDL_WaitThread' }, SDL_GetThreadID: function() { throw 'SDL_GetThreadID' }, - SDL_ThreadID: function() { throw 'SDL_ThreadID' }, + SDL_ThreadID: function() { return 0; }, SDL_AllocRW: function() { throw 'SDL_AllocRW: TODO' }, SDL_CondBroadcast: function() { throw 'SDL_CondBroadcast: TODO' }, SDL_CondWaitTimeout: function() { throw 'SDL_CondWaitTimeout: TODO' }, diff --git a/src/preamble.js b/src/preamble.js index 27016c1439649..ff9200fcf4a25 100644 --- a/src/preamble.js +++ b/src/preamble.js @@ -246,7 +246,7 @@ var EXITSTATUS = 0; var undef = 0; // tempInt is used for 32-bit signed values or smaller. tempBigInt is used // for 32-bit unsigned values or more than 32 bits. TODO: audit all uses of tempInt -var tempValue, tempInt, tempBigInt, tempInt2, tempBigInt2, tempPair, tempBigIntI, tempBigIntR, tempBigIntS, tempBigIntP, tempBigIntD; +var tempValue, tempInt, tempBigInt, tempInt2, tempBigInt2, tempPair, tempBigIntI, tempBigIntR, tempBigIntS, tempBigIntP, tempBigIntD, tempDouble, tempFloat; #if USE_TYPED_ARRAYS == 2 var tempI64, tempI64b; var tempRet0, tempRet1, tempRet2, tempRet3, tempRet4, tempRet5, tempRet6, tempRet7, tempRet8, tempRet9; diff --git a/src/runtime.js b/src/runtime.js index 786ae02187bb5..dedaf5eae6033 100644 --- a/src/runtime.js +++ b/src/runtime.js @@ -381,6 +381,18 @@ var Runtime = { #endif }, + getAsmConst: function(code, numArgs) { + // code is a constant string on the heap, so we can cache these + if (!Runtime.asmConstCache) Runtime.asmConstCache = {}; + var func = Runtime.asmConstCache[code]; + if (func) return func; + var args = []; + for (var i = 0; i < numArgs; i++) { + args.push(String.fromCharCode(36) + i); // $0, $1 etc + } + return Runtime.asmConstCache[code] = eval('(function(' + args.join(',') + '){ ' + Pointer_stringify(code) + ' })'); // new Function does not allow upvars in node + }, + warnOnce: function(text) { if (!Runtime.warnOnce.shown) Runtime.warnOnce.shown = {}; if (!Runtime.warnOnce.shown[text]) { diff --git a/src/shell.js b/src/shell.js index b68e16d918e2f..b41fbb51bb4f9 100644 --- a/src/shell.js +++ b/src/shell.js @@ -91,6 +91,8 @@ else if (ENVIRONMENT_IS_SHELL) { } this['{{{ EXPORT_NAME }}}'] = Module; + + eval("if (typeof gc === 'function' && gc.toString().indexOf('[native code]') > 0) var gc = undefined"); // wipe out the SpiderMonkey shell 'gc' function, which can confuse closure (uses it as a minified name, and it is then initted to a non-falsey value unexpectedly) } else if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) { Module['read'] = function read(url) { diff --git a/system/include/emscripten/emscripten.h b/system/include/emscripten/emscripten.h index ac8809818b69d..ddcbc43a24a1c 100644 --- a/system/include/emscripten/emscripten.h +++ b/system/include/emscripten/emscripten.h @@ -24,16 +24,37 @@ extern "C" { * EM_ASM(window.alert('hai')); * * This also works with asm.js, as it outlines the code (it - * does a function call to reach it). + * does a function call to reach it). It supports newlines, * - * Notes: double-quotes (") are not supported, but you can use + * EM_ASM( + * window.alert('hai')); + * window.alert('bai')); + * ) + * + * Notes: Double-quotes (") are not supported, but you can use * single-quotes (') in js anyhow. * - * you can't access C variables with EM_ASM, use gcc - * inline asm for that, asm("code" : .. etc.) + * You can't access C variables with EM_ASM, nor receive + * a value back. use EM_ASM_INT or EM_ASM_DOUBLE for that */ #define EM_ASM(...) emscripten_asm_const(#__VA_ARGS__) +/* + * Input-output versions of EM_ASM. EM_ASM_INT receives arguments of + * either int or double type and returns an int; EM_ASM_DOUBLE + * receives similar arguments (int or double) but returns a double. + * Arguments arrive as $0, $1 etc; output value should be returned: + * + * int x = EM_ASM_INT({ + * console.log('I received: ' + [$0, $1]); + * return $0 + $1; + * }, calc(), otherCalc()); + * + * Note the {,} + */ +#define EM_ASM_INT(code, ...) emscripten_asm_const_int(#code, __VA_ARGS__) +#define EM_ASM_DOUBLE(code, ...) emscripten_asm_const_double(#code, __VA_ARGS__) + /* * Forces LLVM to not dead-code-eliminate a function. Note that * you still need to use EXPORTED_FUNCTIONS so it stays alive @@ -423,6 +444,8 @@ void emscripten_jcache_printf_(...); /* internal use */ /* Helper API for EM_ASM - do not call this yourself */ void emscripten_asm_const(const char *code); +int emscripten_asm_const_int(const char *code, ...); +double emscripten_asm_const_double(const char *code, ...); #ifdef __cplusplus } diff --git a/tests/freealut/CMakeLists.txt b/tests/freealut/CMakeLists.txt index 640f35bf1bcad..c11680cda4661 100644 --- a/tests/freealut/CMakeLists.txt +++ b/tests/freealut/CMakeLists.txt @@ -72,20 +72,39 @@ ADD_DEFINE("__NO_CTYPE 1") ADD_DEFINITIONS(-DHAVE_CONFIG_H) ADD_DEFINITIONS(-DNDEBUG) -FIND_LIBRARY(OPENAL_LIB NAMES openal openal32 PATHS /usr/lib /usr/local/lib ${OPENAL_LIB_DIR}) -IF(OPENAL_LIB MATCHES "NOTFOUND") - MESSAGE(FATAL_ERROR "OpenAL not installed, cannot build alut - aborting.") -ENDIF(OPENAL_LIB MATCHES "NOTFOUND") - -IF(UNIX) - SET(ADD_LIBS ${ADD_LIBS} m) -ENDIF(UNIX) +if (EMSCRIPTEN) + # Emscripten cannot use FIND_LIBRARY, since that seaches for linkable library files, but Emscripten supports + # OpenAL in core and there are no library files to link against. Instead, use a custom OpenAL package finder + # provided by Emscripten. + FIND_PACKAGE(OpenAL) + + # All the include file checks bloew are no-ops for Emscripten, so for that platform, just define the flags we support. + SET(HAVE_STDINT_H 1) + SET(HAVE_STAT 1) + SET(HAVE_NANOSLEEP 1) + SET(HAVE_TIME_H 1) + ADD_DEFINITIONS(-DHAVE_STDINT_H=1 -DHAVE_STAT=1 -DHAVE_NANOSLEEP=1 -DHAVE_TIME_H=1) + SET(CMAKE_EXECUTABLE_SUFFIX ".bc") +else() + FIND_LIBRARY(OPENAL_LIB NAMES openal openal32 PATHS /usr/lib /usr/local/lib ${OPENAL_LIB_DIR}) + + IF(OPENAL_LIB MATCHES "NOTFOUND") + MESSAGE(FATAL_ERROR "OpenAL not installed, cannot build alut - aborting.") + ENDIF(OPENAL_LIB MATCHES "NOTFOUND") + + IF(UNIX) + SET(ADD_LIBS ${ADD_LIBS} m) + ENDIF(UNIX) +endif() SET(CMAKE_REQUIRED_INCLUDES ${OPENAL_INCLUDE_DIR}) -CHECK_INCLUDE_FILES("AL/alc.h;AL/al.h" AL_HEADERS) -IF(NOT AL_HEADERS) - MESSAGE(FATAL_ERROR "OpenAL header files not found - aborting.") -ENDIF(NOT AL_HEADERS) + +if (NOT EMSCRIPTEN) # Emscripten is a cross-compiler and cannot verify paths of include files with CHECK_INCLUDE_FILES, since that requires building native executables. + CHECK_INCLUDE_FILES("AL/alc.h;AL/al.h" AL_HEADERS) + IF(NOT AL_HEADERS) + MESSAGE(FATAL_ERROR "OpenAL header files not found - aborting.") + ENDIF(NOT AL_HEADERS) +endif() IF(DEFINED OPENAL_INCLUDE_DIR) INCLUDE_DIRECTORIES(${OPENAL_INCLUDE_DIR}) @@ -178,10 +197,18 @@ INSTALL_FILES(/lib/pkgconfig FILES admin/pkgconfig/freealut.pc) IF(BUILD_TESTS) # examples ADD_EXECUTABLE(hello_world examples/hello_world.c) - TARGET_LINK_LIBRARIES(hello_world ${OPENAL_LIB} ${ADD_LIBS} alut) + if (EMSCRIPTEN) + TARGET_LINK_LIBRARIES(hello_world ${OPENAL_LIB} ${ADD_LIBS} alut_static) + else() + TARGET_LINK_LIBRARIES(hello_world ${OPENAL_LIB} ${ADD_LIBS} alut) + endif() ADD_EXECUTABLE(playfile examples/playfile.c) - TARGET_LINK_LIBRARIES(playfile ${OPENAL_LIB} ${ADD_LIBS} alut) + if (EMSCRIPTEN) + TARGET_LINK_LIBRARIES(playfile ${OPENAL_LIB} ${ADD_LIBS} alut_static) + else() + TARGET_LINK_LIBRARIES(playfile ${OPENAL_LIB} ${ADD_LIBS} alut) + endif() SET(TESTS errorstuff diff --git a/tests/runner.py b/tests/runner.py index 7f513635fcb99..34435383a85d8 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -16,7 +16,7 @@ ''' from subprocess import Popen, PIPE, STDOUT -import os, unittest, tempfile, shutil, time, inspect, sys, math, glob, re, difflib, webbrowser, hashlib, threading, platform, BaseHTTPServer, multiprocessing, functools, stat, string +import os, unittest, tempfile, shutil, time, inspect, sys, math, glob, re, difflib, webbrowser, hashlib, threading, platform, BaseHTTPServer, SimpleHTTPServer, multiprocessing, functools, stat, string # Setup @@ -491,22 +491,14 @@ def log_request(code=0, size=0): httpd.serve_forever() # test runner will kill us def server_func(dir, q): - class TestServerHandler(BaseHTTPServer.BaseHTTPRequestHandler): - def do_GET(s): - if 'report_' in s.path: - q.put(s.path) + class TestServerHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): + def do_GET(self): + if 'report_' in self.path: + q.put(self.path) else: - filename = s.path.split('?')[0][1:] - if os.path.exists(filename): - s.send_response(200) - s.send_header("Content-type", "text/html") - s.end_headers() - s.wfile.write(open(filename).read()) - s.wfile.close() - else: - s.send_response(500) - s.send_header("Content-type", "text/html") - s.end_headers() + # Use SimpleHTTPServer default file serving operation for GET. + SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self) + def log_request(code=0, size=0): # don't log; too noisy pass diff --git a/tests/sockets/test_sockets_echo_server.c b/tests/sockets/test_sockets_echo_server.c index dbc912a6a463e..55ace66004924 100644 --- a/tests/sockets/test_sockets_echo_server.c +++ b/tests/sockets/test_sockets_echo_server.c @@ -72,7 +72,16 @@ void main_loop(void *arg) { #if !USE_UDP // for TCP sockets, we may need to accept a connection if (FD_ISSET(server.fd, &fdr)) { +#if TEST_ACCEPT_ADDR + // Do an accept with non-NULL addr and addlen parameters. This tests a fix to a bug in the implementation of + // accept which had a parameter "addrp" but used "addr" internally if addrp was set - giving a ReferenceError. + struct sockaddr_in addr = {0}; + addr.sin_family = AF_INET; + socklen_t addrlen = sizeof(addr); + client.fd = accept(server.fd, (struct sockaddr *) &addr, &addrlen); +#else client.fd = accept(server.fd, NULL, NULL); +#endif assert(client.fd != -1); } #endif diff --git a/tests/test_browser.py b/tests/test_browser.py index 65bccb3867fe4..128820b30320d 100644 --- a/tests/test_browser.py +++ b/tests/test_browser.py @@ -1,12 +1,54 @@ -import BaseHTTPServer, multiprocessing, os, shutil, subprocess, unittest +import BaseHTTPServer, multiprocessing, os, shutil, subprocess, unittest, zlib, webbrowser, time, shlex from runner import BrowserCore, path_from_root from tools.shared import * -''' Enable this code to run in another browser than webbrowser detects as default -def run_in_other_browser(url): - execute(['yourbrowser', url]) -webbrowser.open_new = run_in_other_browser -''' +# User can specify an environment variable EMSCRIPTEN_BROWSER to force the browser test suite to +# run using another browser command line than the default system browser. +emscripten_browser = os.environ.get('EMSCRIPTEN_BROWSER') +if emscripten_browser: + cmd = shlex.split(emscripten_browser) + def run_in_other_browser(url): + Popen(cmd + [url]) + webbrowser.open_new = run_in_other_browser + +def test_chunked_synchronous_xhr_server(support_byte_ranges, chunkSize, data, checksum): + class ChunkedServerHandler(BaseHTTPServer.BaseHTTPRequestHandler): + def sendheaders(s, extra=[], length=len(data)): + s.send_response(200) + s.send_header("Content-Length", str(length)) + s.send_header("Access-Control-Allow-Origin", "http://localhost:8888") + s.send_header("Access-Control-Expose-Headers", "Content-Length, Accept-Ranges") + s.send_header("Content-type", "application/octet-stream") + if support_byte_ranges: + s.send_header("Accept-Ranges", "bytes") + for i in extra: + s.send_header(i[0], i[1]) + s.end_headers() + + def do_HEAD(s): + s.sendheaders() + + def do_OPTIONS(s): + s.sendheaders([("Access-Control-Allow-Headers", "Range")], 0) + + def do_GET(s): + if not support_byte_ranges: + s.sendheaders() + s.wfile.write(data) + else: + (start, end) = s.headers.get("range").split("=")[1].split("-") + start = int(start) + end = int(end) + end = min(len(data)-1, end) + length = end-start+1 + s.sendheaders([],length) + s.wfile.write(data[start:end+1]) + s.wfile.close() + + expectedConns = 11 + httpd = BaseHTTPServer.HTTPServer(('localhost', 11111), ChunkedServerHandler) + for i in range(expectedConns+1): + httpd.handle_request() class browser(BrowserCore): @staticmethod @@ -69,7 +111,6 @@ def test_html_source_map(self): cwd=self.get_dir()).communicate() assert os.path.exists(html_file) assert os.path.exists(html_file + '.map') - import webbrowser, time webbrowser.open_new('file://' + html_file) time.sleep(1) print ''' @@ -85,7 +126,10 @@ def build_native_lzma(self): cwd = os.getcwd() try: os.chdir(path_from_root('third_party', 'lzma.js')) - Popen(['sh', './doit.sh']).communicate() + if WINDOWS and Building.which('mingw32-make'): # On Windows prefer using MinGW make if it exists, otherwise fall back to hoping we have cygwin make. + Popen(['doit.bat']).communicate() + else: + Popen(['sh', './doit.sh']).communicate() finally: os.chdir(cwd) @@ -185,7 +229,7 @@ def test_split_in_source_filenames(self): self.reftest(path_from_root('tests', 'htmltest.png')) output = Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world_sdl.cpp'), '-o', 'something.js', '-g', '--split', '100', '--pre-js', 'reftest.js']).communicate() assert os.path.exists(os.path.join(self.get_dir(), 'something.js')), 'must be main js file' - assert os.path.exists(self.get_dir() + '/something/' + path_from_root('tests', 'hello_world_sdl.cpp.js')), 'must be functions js file' + assert os.path.exists(os.path.join(self.get_dir(), 'something', 'hello_world_sdl.cpp.js')), 'must be functions js file' assert os.path.exists(os.path.join(self.get_dir(), 'something.include.html')), 'must be js include file' open(os.path.join(self.get_dir(), 'something.html'), 'w').write(''' @@ -1159,8 +1203,14 @@ def test_egl_width_height(self): Popen([PYTHON, EMCC, '-O2', os.path.join(self.get_dir(), 'test_egl_width_height.c'), '-o', 'page.html']).communicate() self.run_browser('page.html', 'Should print "(300, 150)" -- the size of the canvas in pixels', '/report_result?1') + def get_freealut_library(self): + if WINDOWS and Building.which('cmake'): + return self.get_library('freealut', os.path.join('hello_world.bc'), configure=['cmake', '.'], configure_args=['-DBUILD_TESTS=ON']) + else: + return self.get_library('freealut', os.path.join('examples', '.libs', 'hello_world.bc'), make_args=['EXEEXT=.bc']) + def test_freealut(self): - programs = self.get_library('freealut', os.path.join('examples', '.libs', 'hello_world.bc'), make_args=['EXEEXT=.bc']) + programs = self.get_freealut_library() for program in programs: assert os.path.exists(program) Popen([PYTHON, EMCC, '-O2', program, '-o', 'page.html']).communicate() @@ -1256,51 +1306,16 @@ def test_chunked_synchronous_xhr(self): chunkSize = 1024 data = os.urandom(10*chunkSize+1) # 10 full chunks and one 1 byte chunk - expectedConns = 11 - import zlib checksum = zlib.adler32(data) - def chunked_server(support_byte_ranges): - class ChunkedServerHandler(BaseHTTPServer.BaseHTTPRequestHandler): - def sendheaders(s, extra=[], length=len(data)): - s.send_response(200) - s.send_header("Content-Length", str(length)) - s.send_header("Access-Control-Allow-Origin", "http://localhost:8888") - s.send_header("Access-Control-Expose-Headers", "Content-Length, Accept-Ranges") - s.send_header("Content-type", "application/octet-stream") - if support_byte_ranges: - s.send_header("Accept-Ranges", "bytes") - for i in extra: - s.send_header(i[0], i[1]) - s.end_headers() - - def do_HEAD(s): - s.sendheaders() - - def do_OPTIONS(s): - s.sendheaders([("Access-Control-Allow-Headers", "Range")], 0) - - def do_GET(s): - if not support_byte_ranges: - s.sendheaders() - s.wfile.write(data) - else: - (start, end) = s.headers.get("range").split("=")[1].split("-") - start = int(start) - end = int(end) - end = min(len(data)-1, end) - length = end-start+1 - s.sendheaders([],length) - s.wfile.write(data[start:end+1]) - s.wfile.close() - httpd = BaseHTTPServer.HTTPServer(('localhost', 11111), ChunkedServerHandler) - for i in range(expectedConns+1): - httpd.handle_request() - - server = multiprocessing.Process(target=chunked_server, args=(True,)) + server = multiprocessing.Process(target=test_chunked_synchronous_xhr_server, args=(True,chunkSize,data,checksum,)) server.start() self.run_browser(main, 'Chunked binary synchronous XHR in Web Workers!', '/report_result?' + str(checksum)) server.terminate() + # Avoid race condition on cleanup, wait a bit so that processes have released file locks so that test tearDown won't + # attempt to rmdir() files in use. + if WINDOWS: + time.sleep(2) def test_glgears(self): self.btest('hello_world_gles.c', reference='gears.png', reference_slack=1, diff --git a/tests/test_core.py b/tests/test_core.py index d7b6cf53e1d9d..67e316e41a510 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -3970,7 +3970,7 @@ def test_inlinejs3(self): #include #include - int main() { + int main(int argc, char **argv) { EM_ASM(Module.print('hello dere1')); EM_ASM( Module.print('hello dere2'); @@ -3981,11 +3981,19 @@ def test_inlinejs3(self): Module.print('hello dere' + 4); ); } + int sum = 0; + for (int i = 0; i < argc*3; i++) { + sum += EM_ASM_INT({ + Module.print('i: ' + [$0, ($1).toFixed(2)]); + return $0*2; + }, i, double(i)/12); + } + printf("sum: %d\n", sum); return 0; } ''' - self.do_run(src, 'hello dere1\nhello dere2\nhello dere3\nhello dere4\nhello dere3\nhello dere4\nhello dere3\nhello dere4\n') + self.do_run(src, 'hello dere1\nhello dere2\nhello dere3\nhello dere4\nhello dere3\nhello dere4\nhello dere3\nhello dere4\ni: 0,0.00\ni: 1,0.08\ni: 2,0.17\nsum: 6\n') def test_memorygrowth(self): if Settings.USE_TYPED_ARRAYS == 0: return self.skip('memory growth is only supported with typed arrays') diff --git a/tests/test_other.py b/tests/test_other.py index 0dd0bd127bc13..1144ec269eb05 100644 --- a/tests/test_other.py +++ b/tests/test_other.py @@ -2074,3 +2074,27 @@ def test_simd(self): Popen([PYTHON, EMCC, path_from_root('tests', 'linpack.c'), '-O2', '-DSP', '--llvm-opts', '''['-O3', '-vectorize', '-vectorize-loops', '-bb-vectorize-vector-bits=128', '-force-vector-width=4']''']).communicate() self.assertContained('Unrolled Single Precision', run_js('a.out.js')) + def test_dependency_file(self): + # Issue 1732: -MMD (and friends) create dependency files that need to be + # copied from the temporary directory. + + open(os.path.join(self.get_dir(), 'test.cpp'), 'w').write(r''' + #include "test.hpp" + + void my_function() + { + } + ''') + open(os.path.join(self.get_dir(), 'test.hpp'), 'w').write(r''' + void my_function(); + ''') + + Popen([PYTHON, EMCC, '-MMD', '-c', os.path.join(self.get_dir(), 'test.cpp'), '-o', + os.path.join(self.get_dir(), 'test.o')]).communicate() + + assert os.path.exists(os.path.join(self.get_dir(), 'test.d')), 'No dependency file generated' + deps = open(os.path.join(self.get_dir(), 'test.d')).read() + head, tail = deps.split( ':', 2 ) + assert 'test.o' in head, 'Invalid dependency target' + assert 'test.cpp' in tail and 'test.hpp' in tail, 'Invalid dependencies generated' + diff --git a/tests/test_sanity.py b/tests/test_sanity.py index a405c3a3ed1f5..c7dd86e412f81 100644 --- a/tests/test_sanity.py +++ b/tests/test_sanity.py @@ -360,7 +360,7 @@ def test_emcc_caching(self): try_delete(basebc_name) # we might need to check this file later try_delete(dcebc_name) # we might need to check this file later for ll_name in ll_names: try_delete(ll_name) - output = self.do([compiler, '-O' + str(i), '-s', 'RELOOP=0', '--llvm-lto', '0', path_from_root('tests', filename)]) + output = self.do([compiler, '-O' + str(i), '-s', 'RELOOP=0', '--llvm-lto', '0', path_from_root('tests', filename), '--save-bc', 'a.bc']) #print output assert INCLUDING_MESSAGE.replace('X', libname) in output if libname == 'libc': diff --git a/tests/test_sockets.py b/tests/test_sockets.py index e1caa1507c56c..1229aa70f5a10 100644 --- a/tests/test_sockets.py +++ b/tests/test_sockets.py @@ -235,7 +235,9 @@ def test_sockets_echo(self): harnesses = [ (WebsockifyServerHarness(os.path.join('sockets', 'test_sockets_echo_server.c'), [sockets_include], 49160), 0), (CompiledServerHarness(os.path.join('sockets', 'test_sockets_echo_server.c'), [sockets_include, '-DTEST_DGRAM=0'], 49161), 0), - (CompiledServerHarness(os.path.join('sockets', 'test_sockets_echo_server.c'), [sockets_include, '-DTEST_DGRAM=1'], 49162), 1) + (CompiledServerHarness(os.path.join('sockets', 'test_sockets_echo_server.c'), [sockets_include, '-DTEST_DGRAM=1'], 49162), 1), + # The following forces non-NULL addr and addlen parameters for the accept call + (CompiledServerHarness(os.path.join('sockets', 'test_sockets_echo_server.c'), [sockets_include, '-DTEST_DGRAM=0', '-DTEST_ACCEPT_ADDR=1'], 49163), 0) ] for harness, datagram in harnesses: diff --git a/third_party/lzma.js/doit.bat b/third_party/lzma.js/doit.bat new file mode 100644 index 0000000000000..17b4f47346c1f --- /dev/null +++ b/third_party/lzma.js/doit.bat @@ -0,0 +1,4 @@ +cd lzip +call mingw32-make lzip +copy /Y lzip.exe ..\lzma-native.exe +cd .. diff --git a/tools/settings_template_readonly.py b/tools/settings_template_readonly.py index 14b45a20d5362..a2a16f191c744 100644 --- a/tools/settings_template_readonly.py +++ b/tools/settings_template_readonly.py @@ -18,6 +18,8 @@ TEMP_DIR = '{{{ TEMP }}}' +CRUNCH = os.path.expanduser(os.getenv('CRUNCH') or 'crunch') # executable + #CLOSURE_COMPILER = '..' # define this to not use the bundled version ######################################################################################################## diff --git a/tools/shared.py b/tools/shared.py index 0a6740b9d0680..0783dc752be25 100644 --- a/tools/shared.py +++ b/tools/shared.py @@ -307,7 +307,7 @@ def find_temp_directory(): # we re-check sanity when the settings are changed) # We also re-check sanity and clear the cache when the version changes -EMSCRIPTEN_VERSION = '1.7.7' +EMSCRIPTEN_VERSION = '1.7.8' def generate_sanity(): return EMSCRIPTEN_VERSION + '|' + get_llvm_target() + '|' + LLVM_ROOT @@ -1029,7 +1029,7 @@ def link(files, target, force_archive_contents=False): dirname = os.path.dirname(content) if dirname and not os.path.exists(dirname): os.makedirs(dirname) - Popen([LLVM_AR, 'x', f], stdout=PIPE).communicate() # if absolute paths, files will appear there. otherwise, in this directory + Popen([LLVM_AR, 'xo', f], stdout=PIPE).communicate() # if absolute paths, files will appear there. otherwise, in this directory contents = map(lambda content: os.path.join(temp_dir, content), contents) contents = filter(os.path.exists, map(os.path.abspath, contents)) added_contents = set() diff --git a/tools/split.py b/tools/split.py index f9e338aa8303d..b15b1716c33ba 100644 --- a/tools/split.py +++ b/tools/split.py @@ -1,16 +1,13 @@ import sys import os +from sets import Set def split_javascript_file(input_filename, output_filename_prefix, max_part_size_in_bytes): try: - # Contains the entire Emscripten generated Javascript code - input_file = open(input_filename,'r') - # Javascript main file. On execution, this file needs to be loaded at last (!) output_main_filename = output_filename_prefix + ".js" output_main_file = open(output_main_filename,'w') - # File with HTML script tags to load the Javascript files in HTML later on output_html_include_file = open(output_filename_prefix + ".include.html",'w') @@ -22,8 +19,19 @@ def split_javascript_file(input_filename, output_filename_prefix, max_part_size_ function_buckets = {}; output_part_file = None - + + # Locate names of all the source files (.c/.cpp) that produced output to the .js file. + source_files = Set() + for line in open(input_filename,'r'): + if line.startswith("//FUNCTION_END_MARKER_OF_SOURCE_FILE_"): + associated_source_file_base = line[len("//FUNCTION_END_MARKER_OF_SOURCE_FILE_"):len(line)-1] + if not associated_source_file_base == "NO_SOURCE": + source_files.add(os.path.dirname(os.path.abspath(os.path.realpath(associated_source_file_base)))) + + common_source_file_prefix = os.path.commonprefix(list(source_files)) + # Iterate over Javascript source; write main file; parse function declarations. + input_file = open(input_filename,'r') for line in input_file: if line == "//FUNCTION_BEGIN_MARKER\n": js_function = "//Func\n" @@ -36,8 +44,8 @@ def split_javascript_file(input_filename, output_filename_prefix, max_part_size_ associated_source_file_base = output_filename_prefix + "_functions"; else: # Functions with a known associated source file are stored in a file in the directory `output_filename_prefix` - associated_source_file_base = output_filename_prefix + os.path.realpath(associated_source_file_base) - + associated_source_file_base = os.path.join(output_filename_prefix, os.path.relpath(os.path.abspath(os.path.realpath(associated_source_file_base)), common_source_file_prefix)) + associated_source_file_base_lower = associated_source_file_base.lower() # Add the function to its respective file @@ -68,7 +76,7 @@ def split_javascript_file(input_filename, output_filename_prefix, max_part_size_ output_part_file = None for js_function in function_buckets[associated_source_file_base][1]: if output_part_file is None: - output_html_include_file.write("") + output_html_include_file.write("") output_part_file = open(js_source_file,'w') output_part_file.write(js_function) @@ -85,13 +93,13 @@ def split_javascript_file(input_filename, output_filename_prefix, max_part_size_ # Write the main Javascript file at last to the HTML includes because this file contains the code to start # the execution of the generated Emscripten application and requires all the extracted functions. - output_html_include_file.write("") + output_html_include_file.write("") except Exception, e: print >> sys.stderr, 'error: Splitting of Emscripten generated Javascript failed: %s' % str(e) finally: - if input_file is not None: input_file.close() - if output_main_file is not None: output_main_file.close() - if output_part_file is not None: output_part_file.close() - if output_html_include_file is not None: output_html_include_file.close() \ No newline at end of file + if input_file is not None: input_file.close() + if output_main_file is not None: output_main_file.close() + if output_part_file is not None: output_part_file.close() + if output_html_include_file is not None: output_html_include_file.close() \ No newline at end of file