diff options
-rw-r--r-- | cmake/Modules/FindOpenAL.cmake | 26 | ||||
-rw-r--r-- | cmake/Platform/Emscripten.cmake | 7 | ||||
-rwxr-xr-x | emcc | 15 | ||||
-rw-r--r-- | tests/freealut/CMakeLists.txt | 55 | ||||
-rwxr-xr-x | tests/runner.py | 24 | ||||
-rw-r--r-- | tests/test_browser.py | 101 | ||||
-rw-r--r-- | third_party/lzma.js/doit.bat | 4 | ||||
-rw-r--r-- | tools/settings_template_readonly.py | 2 | ||||
-rw-r--r-- | tools/split.py | 34 |
9 files changed, 176 insertions, 92 deletions
diff --git a/cmake/Modules/FindOpenAL.cmake b/cmake/Modules/FindOpenAL.cmake new file mode 100644 index 00000000..3170966d --- /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 e1e54ccf..c30632ca 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 "") @@ -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 diff --git a/tests/freealut/CMakeLists.txt b/tests/freealut/CMakeLists.txt index 640f35bf..c11680cd 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 7f513635..34435383 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -16,7 +16,7 @@ so you may prefer to use fewer cores here. ''' 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 harness_server_func(q): 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/test_browser.py b/tests/test_browser.py index 65bccb38..f2bcaa93 100644 --- a/tests/test_browser.py +++ b/tests/test_browser.py @@ -1,4 +1,4 @@ -import BaseHTTPServer, multiprocessing, os, shutil, subprocess, unittest +import BaseHTTPServer, multiprocessing, os, shutil, subprocess, unittest, zlib from runner import BrowserCore, path_from_root from tools.shared import * @@ -8,6 +8,45 @@ def run_in_other_browser(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 def audio(): @@ -85,7 +124,10 @@ If manually bisecting: 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 +227,7 @@ If manually bisecting: 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 +1201,14 @@ keydown(100);keyup(100); // trigger the end 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 +1304,16 @@ keydown(100);keyup(100); // trigger the end 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/third_party/lzma.js/doit.bat b/third_party/lzma.js/doit.bat new file mode 100644 index 00000000..17b4f473 --- /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 14b45a20..a2a16f19 100644 --- a/tools/settings_template_readonly.py +++ b/tools/settings_template_readonly.py @@ -18,6 +18,8 @@ JAVA = 'java' # executable 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/split.py b/tools/split.py index f9e338aa..b15b1716 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("<script type=\"text/javascript\" src=\"" + js_source_file + "\"></script>") + output_html_include_file.write("<script type=\"text/javascript\" src=\"" + js_source_file.replace('\\', '/') + "\"></script>") 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("<script type=\"text/javascript\" src=\"" + output_main_filename + "\"></script>") + output_html_include_file.write("<script type=\"text/javascript\" src=\"" + output_main_filename.replace('\\', '/') + "\"></script>") 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 |