aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xemcc268
-rw-r--r--src/jsifier.js5
-rw-r--r--src/library.js13
-rw-r--r--src/library_gc.js14
-rw-r--r--src/library_gl.js13
-rw-r--r--src/library_sdl.js18
-rw-r--r--src/postamble.js4
-rw-r--r--src/preamble.js3
-rw-r--r--src/settings.js2
-rw-r--r--system/include/emscripten.h5
-rw-r--r--tests/gl_matrix_identity.c129
-rwxr-xr-xtests/runner.py34
-rw-r--r--tools/file_packager.py290
-rw-r--r--tools/shared.py26
14 files changed, 544 insertions, 280 deletions
diff --git a/emcc b/emcc
index 6471f8f3..92d52e15 100755
--- a/emcc
+++ b/emcc
@@ -77,15 +77,7 @@ emcc can be influenced by a few environment variables:
import os, sys, shutil, tempfile, subprocess, shlex
from subprocess import PIPE, STDOUT
from tools import shared
-
-def execute(cmd, *args, **kw):
- try:
- return subprocess.Popen(cmd, *args, **kw).communicate() # let compiler frontend print directly, so colors are saved (PIPE kills that)
- except:
- if not isinstance(cmd, str):
- cmd = ' '.join(cmd)
- print >> sys.stderr, 'Invoking Process failed: <<< ' + cmd + ' >>>'
- raise
+from tools.shared import Compression, execute
# Mapping of emcc opt levels to llvm opt levels. We use llvm opt level 3 in emcc opt
# levels 2 and 3 (emcc 3 is unsafe opts, so unsuitable for the only level to get
@@ -220,9 +212,14 @@ Options that are modified or new in %s include:
compiled code asynchronously. Otherwise
similar to --embed-file, except that this
option is only relevant when generating
- HTML (it uses asynchronous binary XHRs).
+ HTML (it uses asynchronous binary XHRs),
+ or JS that will be used in a web page.
If a directory is passed here, its entire
contents will be preloaded.
+ Preloaded files are stored in filename.data,
+ where filename.html is the main file you
+ are compiling to. To run your code, you
+ will need both the .html and the .data.
--compression <codec> Compress both the compiled code and embedded/
preloaded files. <codec> should be a triple,
@@ -332,16 +329,12 @@ if EMMAKEN_CFLAGS: CC_ADDITIONAL_ARGS += shlex.split(EMMAKEN_CFLAGS)
# ---------------- Utilities ---------------
SOURCE_SUFFIXES = ('.c', '.cpp', '.cxx', '.cc')
-BITCODE_SUFFIXES = ('.bc', '.o')
+BITCODE_SUFFIXES = ('.bc', '.o', '.obj')
DYNAMICLIB_SUFFIXES = ('.dylib', '.so', '.dll')
STATICLIB_SUFFIXES = ('.a',)
ASSEMBLY_SUFFIXES = ('.ll',)
LIB_PREFIXES = ('', 'lib')
-IMAGE_SUFFIXES = ('.jpg', '.png', '.bmp')
-AUDIO_SUFFIXES = ('.ogg', '.wav', '.mp3')
-AUDIO_MIMETYPES = { 'ogg': 'audio/ogg', 'wav': 'audio/wav', 'mp3': 'audio/mpeg' }
-
def suffix(name):
return name.split('.')[-1]
@@ -413,21 +406,6 @@ else:
def in_temp(name):
return os.path.join(temp_dir, name)
-class Compression:
- on = False
-
- @staticmethod
- def compressed_name(filename):
- return filename + '.compress'
-
- @staticmethod
- def compress(filename):
- execute(Compression.encoder, stdin=open(filename, 'rb'), stdout=open(Compression.compressed_name(filename), 'wb'))
-
- @staticmethod
- def worth_it(original, compressed):
- return compressed < original - 1500 # save at least one TCP packet or so
-
try:
call = CXX if use_cxx else CC
@@ -443,7 +421,8 @@ try:
pre_js = None
post_js = None
minify_whitespace = None
- data_files = []
+ preload_files = []
+ embed_files = []
compression = None
ignore_dynamic_linking = False
shell_path = shared.path_from_root('src', 'shell.html')
@@ -497,12 +476,12 @@ try:
newargs[i+1] = ''
elif newargs[i].startswith('--embed-file'):
check_bad_eq(newargs[i])
- data_files.append({ 'name': newargs[i+1], 'mode': 'embed' })
+ embed_files.append(newargs[i+1])
newargs[i] = ''
newargs[i+1] = ''
elif newargs[i].startswith('--preload-file'):
check_bad_eq(newargs[i])
- data_files.append({ 'name': newargs[i+1], 'mode': 'preload' })
+ preload_files.append(newargs[i+1])
newargs[i] = ''
newargs[i+1] = ''
elif newargs[i].startswith('--compression'):
@@ -722,9 +701,6 @@ try:
if DEBUG: print >> sys.stderr, 'emcc: will generate JavaScript'
- if final_suffix == 'html':
- shared.Settings.GENERATING_HTML = 1
-
extra_files_to_link = []
if not LEAVE_INPUTS_RAW and not AUTODEBUG:
@@ -871,216 +847,18 @@ try:
if DEBUG: save_intermediate('original')
# Embed and preload files
- if len(data_files) > 0:
+ if len(preload_files) + len(embed_files) > 0:
if DEBUG: print >> sys.stderr, 'emcc: setting up files'
- code = ''
-
- if final_suffix == 'html':
- code += '''
- var BlobBuilder = typeof MozBlobBuilder != "undefined" ? MozBlobBuilder : (typeof WebKitBlobBuilder != "undefined" ? WebKitBlobBuilder : console.log("warning: cannot build blobs"));
- var URLObject = typeof window != "undefined" ? (window.URL ? window.URL : window.webkitURL) : console.log("warning: cannot create object URLs");
- var hasBlobConstructor;
- try {
- new Blob();
- hasBlobConstructor = true;
- } catch(e) {
- hasBlobConstructor = false;
- console.log("warning: no blob constructor, cannot create blobs with mimetypes");
- }
-'''
-
- code += 'var preloadedImages = {}; // maps url to image data\n'
- code += 'var preloadedAudios = {}; // maps url to audio data\n'
-
- # Expand directories into individual files
- def add(mode, dirname, names):
- for name in names:
- fullname = os.path.join(dirname, name)
- if not os.path.isdir(fullname):
- data_files.append({ 'name': fullname, 'mode': mode })
-
- for file_ in data_files:
- if os.path.isdir(file_['name']):
- os.path.walk(file_['name'], add, file_['mode'])
- data_files = filter(lambda file_: not os.path.isdir(file_['name']), data_files)
-
- for file_ in data_files:
- file_['name'] = file_['name'].replace(os.path.sep, '/')
- file_['net_name'] = file_['name']
-
- data_target = unsuffixed(target) + '.data'
-
- # Set up folders
- partial_dirs = []
- for file_ in data_files:
- dirname = os.path.dirname(file_['name'])
- dirname = dirname.lstrip('/') # absolute paths start with '/', remove that
- if dirname != '':
- parts = dirname.split('/')
- for i in range(len(parts)):
- partial = '/'.join(parts[:i+1])
- if partial not in partial_dirs:
- code += '''FS.createFolder('/%s', '%s', true, false);\n''' % ('/'.join(parts[:i]), parts[i])
- partial_dirs.append(partial)
-
- if final_suffix == 'html':
- # Bundle all datafiles into one archive. Avoids doing lots of simultaneous XHRs which has overhead.
- data = open(data_target, 'wb')
- start = 0
- for file_ in data_files:
- file_['data_start'] = start
- curr = open(file_['name'], 'rb').read()
- file_['data_end'] = start + len(curr)
- start += len(curr)
- data.write(curr)
- data.close()
- if Compression.on:
- Compression.compress(data_target)
-
- # Data requests - for getting a block of data out of the big archive - have a similar API to XHRs
- code += '''
- function DataRequest() {}
- DataRequest.prototype = {
- requests: {},
- open: function(mode, name) {
- this.requests[name] = this;
- },
- send: function() {}
- };
- '''
-
- counter = 0
- for file_ in data_files:
- filename = file_['name']
- if file_['mode'] == 'embed':
- # Embed
- code += '''FS.createDataFile('/', '%s', %s, true, true);\n''' % (os.path.basename(filename), str(map(ord, open(filename, 'rb').read())))
- elif file_['mode'] == 'preload':
- # Preload
- assert final_suffix == 'html', 'Can only preload files when generating HTML'
-
- varname = 'filePreload%d' % counter
- counter += 1
- image = filename.endswith(IMAGE_SUFFIXES)
- audio = filename.endswith(AUDIO_SUFFIXES)
-
- if image:
- finish = '''
- var bb = new BlobBuilder();
- bb.append(byteArray.buffer);
- var b = bb.getBlob();
- var url = URLObject.createObjectURL(b);
- var img = new Image();
- img.onload = function() {
- assert(img.complete, 'Image %(filename)s could not be decoded');
- var canvas = document.createElement('canvas');
- canvas.width = img.width;
- canvas.height = img.height;
- var ctx = canvas.getContext('2d');
- ctx.drawImage(img, 0, 0);
- preloadedImages['%(filename)s'] = canvas;
- URLObject.revokeObjectURL(url);
- removeRunDependency();
- };
- img.onerror = function(event) {
- console.log('Image %(filename)s could not be decoded');
- };
- img.src = url;
-''' % { 'filename': filename }
- elif audio:
- # Need actual blob constructor here, to set the mimetype or else audios fail to decode
- finish = '''
- if (hasBlobConstructor) {
- var b = new Blob([byteArray.buffer], { type: '%(mimetype)s' });
- var url = URLObject.createObjectURL(b); // XXX we never revoke this!
- var audio = new Audio();
- audio.removedDependency = false;
- audio['oncanplaythrough'] = function() { // XXX string for closure
- audio['oncanplaythrough'] = null;
- preloadedAudios['%(filename)s'] = audio;
- if (!audio.removedDependency) {
- removeRunDependency();
- audio.removedDependency = true;
- }
- };
- audio.onerror = function(event) {
- if (!audio.removedDependency) {
- console.log('Audio %(filename)s could not be decoded or timed out trying to decode');
- removeRunDependency();
- audio.removedDependency = true;
- }
- };
- setTimeout(audio.onerror, 2000); // workaround for chromium bug 124926 (still no audio with this, but at least we don't hang)
- audio.src = url;
- } else {
- preloadedAudios['%(filename)s'] = new Audio(); // empty shim
- removeRunDependency();
- }
-''' % { 'filename': filename, 'mimetype': AUDIO_MIMETYPES[suffix(filename)] }
- else:
- finish = 'removeRunDependency();\n'
-
- code += '''
- var %(varname)s = new %(request)s();
- %(varname)s.open('GET', '%(netname)s', true);
- %(varname)s.responseType = 'arraybuffer';
- %(varname)s.onload = function() {
- var arrayBuffer = %(varname)s.response;
- assert(arrayBuffer, 'Loading file %(filename)s failed.');
- var byteArray = arrayBuffer.byteLength ? new Uint8Array(arrayBuffer) : arrayBuffer;
- FS.createDataFile('/%(dirname)s', '%(basename)s', byteArray, true, true);
- %(finish)s
- };
- addRunDependency();
- %(varname)s.send(null);
-''' % {
- 'request': 'DataRequest', # In the past we also supported XHRs here
- 'varname': varname,
- 'filename': filename,
- 'netname': file_['net_name'],
- 'dirname': os.path.dirname(filename),
- 'basename': os.path.basename(filename),
- 'finish': finish
- }
- else:
- assert 0
-
- if final_suffix == 'html':
- # Get the big archive and split it up
- use_data = ''
- for file_ in data_files:
- if file_['mode'] == 'preload':
- use_data += '''
- curr = DataRequest.prototype.requests['%s'];
- curr.response = byteArray.subarray(%d,%d);
- curr.onload();
- ''' % (file_['name'], file_['data_start'], file_['data_end'])
- use_data += ' removeRunDependency();\n'
-
- if Compression.on:
- use_data = '''
- Module["decompress"](byteArray, function(decompressed) {
- byteArray = new Uint8Array(decompressed);
- %s
- });
- ''' % use_data
-
- code += '''
- var dataFile = new XMLHttpRequest();
- dataFile.open('GET', '%s', true);
- dataFile.responseType = 'arraybuffer';
- dataFile.onload = function() {
- var arrayBuffer = dataFile.response;
- assert(arrayBuffer, 'Loading data file failed.');
- var byteArray = new Uint8Array(arrayBuffer);
- var curr;
- %s
- };
- addRunDependency();
- dataFile.send(null);
- if (Module['setStatus']) Module['setStatus']('Downloading...');
- ''' % (Compression.compressed_name(data_target) if Compression.on else data_target, use_data)
-
+ file_args = []
+ if len(preload_files) > 0:
+ file_args.append('--preload')
+ file_args += preload_files
+ if len(embed_files) > 0:
+ file_args.append('--embed')
+ file_args += embed_files
+ if Compression.on:
+ file_args += ['--compress', Compression.encoder, Compression.decoder, Compression.js_name]
+ code = execute(shared.ENV_PREFIX + ['python', shared.FILE_PACKAGER, unsuffixed(target) + '.data'] + file_args, stdout=PIPE)[0]
src = open(final).read().replace('// {{PRE_RUN_ADDITIONS}}', code)
final += '.files.js'
open(final, 'w').write(src)
diff --git a/src/jsifier.js b/src/jsifier.js
index 1d18f292..e76407b2 100644
--- a/src/jsifier.js
+++ b/src/jsifier.js
@@ -71,10 +71,7 @@ function JSify(data, functionsOnly, givenFunctions) {
}
}
} else {
- libFuncsToInclude = ['memcpy', 'memset', 'malloc', 'free'];
- }
- if (GENERATING_HTML) {
- libFuncsToInclude.push('$Browser');
+ libFuncsToInclude = ['memcpy', 'memset', 'malloc', 'free', '$Browser'];
}
libFuncsToInclude.forEach(function(ident) {
data.functionStubs.push({
diff --git a/src/library.js b/src/library.js
index 9ebe926a..cb94fccb 100644
--- a/src/library.js
+++ b/src/library.js
@@ -28,7 +28,14 @@ LibraryManager.library = {
$FS__deps: ['$ERRNO_CODES', '__setErrNo', 'stdin', 'stdout', 'stderr', '_impure_ptr'],
$FS__postset: '__ATINIT__.unshift({ func: function() { if (!Module["noFSInit"] && !FS.init.initialized) FS.init() } });' +
'__ATMAIN__.push({ func: function() { FS.ignorePermissions = false } });' +
- '__ATEXIT__.push({ func: function() { FS.quit() } });',
+ '__ATEXIT__.push({ func: function() { FS.quit() } });' +
+ // export some names through closure
+ 'Module["FS_createFolder"] = FS.createFolder;' +
+ 'Module["FS_createPath"] = FS.createPath;' +
+ 'Module["FS_createDataFile"] = FS.createDataFile;' +
+ 'Module["FS_createLazyFile"] = FS.createLazyFile;' +
+ 'Module["FS_createLink"] = FS.createLink;' +
+ 'Module["FS_createDevice"] = FS.createDevice;',
$FS: {
// The path to the current folder.
currentPath: '/',
@@ -6196,6 +6203,10 @@ LibraryManager.library = {
return eval(Pointer_stringify(ptr));
},
+ emscripten_random: function() {
+ return Math.random();
+ },
+
$Profiling: {
max_: 0,
times: null,
diff --git a/src/library_gc.js b/src/library_gc.js
index ccf6656d..bf0a6aff 100644
--- a/src/library_gc.js
+++ b/src/library_gc.js
@@ -16,13 +16,15 @@ if (GC_SUPPORT) {
init: function() {
assert(!GC.initted);
GC.initted = true;
-#if GENERATING_HTML
- setInterval(function() {
- GC.maybeCollect();
- }, 1000);
-#else
- // No HTML intervals, so you need to call GC.maybeCollect() or GC.collect() manually
+ if (ENVIRONMENT_IS_WEB) {
+ setInterval(function() {
+ GC.maybeCollect();
+ }, 1000);
+ } else {
+#if ASSERTIONS
+ Module.print('No HTML intervals, so you need to call GC.maybeCollect() or GC.collect() manually');
#endif
+ }
},
malloc: function(bytes, clear, scannable) {
diff --git a/src/library_gl.js b/src/library_gl.js
index 153473f1..1b6ec5c0 100644
--- a/src/library_gl.js
+++ b/src/library_gl.js
@@ -897,13 +897,14 @@ var LibraryGL = {
// XXX some of these ignored capabilities may lead to incorrect rendering, if we do not emulate them in shaders
var ignoredCapabilities = {
- 0x0DE1: 1, // GL_TEXTURE_2D
0x0B20: 1, // GL_LINE_SMOOTH
0x0B60: 1, // GL_FOG
- 0x8513: 1, // GL_TEXTURE_CUBE_MAP
0x0BA1: 1, // GL_NORMALIZE
0x0C60: 1, // GL_TEXTURE_GEN_S
- 0x0C61: 1 // GL_TEXTURE_GEN_T
+ 0x0C61: 1, // GL_TEXTURE_GEN_T
+ 0x0DE1: 1, // GL_TEXTURE_2D
+ 0x8513: 1, // GL_TEXTURE_CUBE_MAP
+ 0x2A02: 1 // GL_POLYGON_OFFSET_LINE
};
_glEnable = function(cap) {
// Clean up the renderer on any change to the rendering state. The optimization of
@@ -1683,7 +1684,9 @@ var LibraryGL = {
// Initialize matrix library
GL.immediate.matrix['m'] = GL.immediate.matrix.lib.mat4.create();
+ GL.immediate.matrix.lib.mat4.identity(GL.immediate.matrix['m']);
GL.immediate.matrix['p'] = GL.immediate.matrix.lib.mat4.create();
+ GL.immediate.matrix.lib.mat4.identity(GL.immediate.matrix['p']);
for (var i = 0; i < GL.immediate.MAX_TEXTURES; i++) {
GL.immediate.matrix['t' + i] = GL.immediate.matrix.lib.mat4.create();
}
@@ -2220,8 +2223,8 @@ var LibraryGL = {
// Simple pass-through functions. Starred ones have return values. [X] ones have X in the C name but not in the JS name
[[0, 'shadeModel getError* finish flush'],
- [1, 'clearDepth clearDepth[f] depthFunc enable disable frontFace cullFace clear enableVertexAttribArray disableVertexAttribArray lineWidth clearStencil depthMask stencilMask checkFramebufferStatus* generateMipmap activeTexture blendEquation polygonOffset sampleCoverage isEnabled*'],
- [2, 'blendFunc blendEquationSeparate depthRange depthRange[f] stencilMaskSeparate hint'],
+ [1, 'clearDepth clearDepth[f] depthFunc enable disable frontFace cullFace clear enableVertexAttribArray disableVertexAttribArray lineWidth clearStencil depthMask stencilMask checkFramebufferStatus* generateMipmap activeTexture blendEquation sampleCoverage isEnabled*'],
+ [2, 'blendFunc blendEquationSeparate depthRange depthRange[f] stencilMaskSeparate hint polygonOffset'],
[3, 'texParameteri texParameterf drawArrays vertexAttrib2f stencilFunc stencilOp'],
[4, 'viewport clearColor scissor vertexAttrib3f colorMask drawElements renderbufferStorage blendFuncSeparate blendColor stencilFuncSeparate stencilOpSeparate'],
[5, 'vertexAttrib4f'],
diff --git a/src/library_sdl.js b/src/library_sdl.js
index 7011e679..6250d6e3 100644
--- a/src/library_sdl.js
+++ b/src/library_sdl.js
@@ -934,7 +934,7 @@ var LibrarySDL = {
// Convert the path to relative
filename = filename.substr(1);
}
- var raw = preloadedImages[filename];
+ var raw = Module["preloadedImages"][filename];
if (!raw) {
Runtime.warnOnce('Cannot find preloaded image ' + filename);
return 0;
@@ -1058,7 +1058,7 @@ var LibrarySDL = {
Mix_LoadWAV_RW: function(filename, freesrc) {
filename = FS.standardizePath(Pointer_stringify(filename));
- var raw = preloadedAudios[filename];
+ var raw = Module["preloadedAudios"][filename];
if (!raw) {
Runtime.warnOnce('Cannot find preloaded audio ' + filename);
return 0;
@@ -1117,8 +1117,12 @@ var LibrarySDL = {
return 0;
},
+ Mix_HookMusicFinished__deps: ['Mix_HaltMusic'],
Mix_HookMusicFinished: function(func) {
SDL.hookMusicFinished = func;
+ if (SDL.music) { // ensure the callback will be called, if a music is already playing
+ SDL.music['onended'] = _Mix_HaltMusic;
+ }
},
Mix_VolumeMusic: function(func) {
@@ -1129,12 +1133,14 @@ var LibrarySDL = {
Mix_FreeMusic: 'Mix_FreeChunk',
+ Mix_PlayMusic__deps: ['Mix_HaltMusic'],
Mix_PlayMusic: function(id, loops) {
loops = Math.max(loops, 1);
var audio = SDL.audios[id].audio;
if (!audio) return 0;
audio.loop = loops != 1; // TODO: handle N loops for finite N
audio.play();
+ audio['onended'] = _Mix_HaltMusic; // will send callback
SDL.music = audio;
return 0;
},
@@ -1169,6 +1175,14 @@ var LibrarySDL = {
Mix_FadeOutMusic: 'Mix_HaltMusic', // XXX ignore fading out effect
+ Mix_PlayingMusic: function() {
+ return (SDL.music && !SDL.music.paused) ? 1 : 0;
+ },
+
+ Mix_PausedMusic: function() {
+ return (SDL.music && SDL.music.paused) ? 1 : 0;
+ },
+
// SDL TTF
TTF_Init: function() { return 0 },
diff --git a/src/postamble.js b/src/postamble.js
index ea03391c..56586487 100644
--- a/src/postamble.js
+++ b/src/postamble.js
@@ -56,7 +56,6 @@ function run(args) {
return ret;
}
-#if GENERATING_HTML
if (Module['setStatus']) {
Module['setStatus']('Running...');
setTimeout(function() {
@@ -69,9 +68,6 @@ function run(args) {
} else {
return doRun();
}
-#else
- return doRun();
-#endif
}
Module['run'] = run;
diff --git a/src/preamble.js b/src/preamble.js
index 541b4bb5..ae00b796 100644
--- a/src/preamble.js
+++ b/src/preamble.js
@@ -326,6 +326,7 @@ function ccall(ident, returnType, argTypes, args) {
var stack = 0;
function toC(value, type) {
if (type == 'string') {
+ if (value === null || value === undefined || value === 0) return 0; // null string
if (!stack) stack = Runtime.stackSave();
var ret = Runtime.stackAlloc(value.length+1);
writeStringToMemory(value, ret);
@@ -819,6 +820,7 @@ function addRunDependency() {
Module['monitorRunDependencies'](runDependencies);
}
}
+Module['addRunDependency'] = addRunDependency;
function removeRunDependency() {
runDependencies--;
if (Module['monitorRunDependencies']) {
@@ -826,6 +828,7 @@ function removeRunDependency() {
}
if (runDependencies == 0) run();
}
+Module['removeRunDependency'] = removeRunDependency;
// === Body ===
diff --git a/src/settings.js b/src/settings.js
index e0286213..2526081b 100644
--- a/src/settings.js
+++ b/src/settings.js
@@ -205,8 +205,6 @@ var LINKABLE = 0; // If set to 1, this file can be linked with others, either as
// LINKABLE of 0 is very useful in that we can reduce the size of the
// generated code very significantly, by removing everything not actually used.
-var GENERATING_HTML = 0; // Set to 1 when generating .html and not just .js
-
var RUNTIME_TYPE_INFO = 0; // Whether to expose type info to the script at run time. This
// increases the size of the generated script, but allows you
// to more easily perform operations from handwritten JS on
diff --git a/system/include/emscripten.h b/system/include/emscripten.h
index 4da31ec3..70524835 100644
--- a/system/include/emscripten.h
+++ b/system/include/emscripten.h
@@ -80,6 +80,11 @@ void emscripten_set_canvas_size(int width, int height);
float emscripten_get_now();
/*
+ * Simple random number generation in [0, 1), maps to Math.random().
+ */
+float emscripten_random();
+
+/*
* This macro-looking function will cause Emscripten to
* generate a comment in the generated code.
* XXX This is deprecated for now, because it requires us to
diff --git a/tests/gl_matrix_identity.c b/tests/gl_matrix_identity.c
new file mode 100644
index 00000000..98b1c21f
--- /dev/null
+++ b/tests/gl_matrix_identity.c
@@ -0,0 +1,129 @@
+/*
+THIS WORK, INCLUDING THE SOURCE CODE, DOCUMENTATION
+AND RELATED MEDIA AND DATA, IS PLACED INTO THE PUBLIC DOMAIN.
+
+THE ORIGINAL AUTHOR IS KYLE FOLEY.
+
+THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY
+OF ANY KIND, NOT EVEN THE IMPLIED WARRANTY OF
+MERCHANTABILITY. THE AUTHOR OF THIS SOFTWARE,
+ASSUMES _NO_ RESPONSIBILITY FOR ANY CONSEQUENCE
+RESULTING FROM THE USE, MODIFICATION, OR
+REDISTRIBUTION OF THIS SOFTWARE.
+*/
+
+#if !EMSCRIPTEN
+#define USE_GLEW 1
+#endif
+
+#if USE_GLEW
+#include "GL/glew.h"
+#endif
+
+#include "SDL/SDL.h"
+#if !USE_GLEW
+#include "SDL/SDL_opengl.h"
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+void verify() {
+ int width = 640, height = 480;
+ unsigned char *data = (unsigned char*)malloc(width*height*4);
+ glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, data);
+ int sum = 0;
+ for (int x = 0; x < width*height*4; x++) {
+ if (x % 4 != 3) sum += x * data[x];
+ }
+#if EMSCRIPTEN
+ int result = sum;
+ REPORT_RESULT();
+#endif
+}
+
+int main(int argc, char *argv[])
+{
+ SDL_Surface *screen;
+ if ( SDL_Init(SDL_INIT_VIDEO) != 0 ) {
+ printf("Unable to initialize SDL: %s\n", SDL_GetError());
+ return 1;
+ }
+
+ SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
+ screen = SDL_SetVideoMode( 640, 480, 24, SDL_OPENGL );
+ if ( !screen ) {
+ printf("Unable to set video mode: %s\n", SDL_GetError());
+ return 1;
+ }
+
+ // Create a texture
+
+ GLuint texture;
+ glGenTextures( 1, &texture );
+ glBindTexture( GL_TEXTURE_2D, texture );
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
+ GLubyte textureData[] = { 0x7f, 0, 0,
+ 0, 0xff, 0,
+ 0x7f, 0, 0,
+ 0, 0xff, 0};
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+ glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB, 2, 2, 0,
+ GL_RGB, GL_UNSIGNED_BYTE, textureData );
+
+
+ // BEGIN
+
+#if USE_GLEW
+ glewInit();
+#endif
+
+ glClearColor( 0, 0, 0.5, 1.0 );
+ glClear( GL_COLOR_BUFFER_BIT );
+
+ glColor4f(0.8, 0.8, 0.8, 1);
+
+ glDisable(GL_DEPTH_TEST);
+ glDisable(GL_CULL_FACE);
+ glEnable(GL_TEXTURE_2D);
+ glDisable(GL_BLEND);
+
+ const int kRowSize = 20;
+ GLuint buffer;
+ glGenBuffers(1, &buffer);
+ glBindBuffer(GL_ARRAY_BUFFER, buffer);
+ float fbuf[] = {0, 1, 0, 0, 1,
+ 1, 1, 0, 1, 1,
+ 1, 0, 0, 1, 0,
+ 0, 1, 0, 0, 1,
+ 1, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0};
+ glBufferData(GL_ARRAY_BUFFER, sizeof(fbuf) * sizeof(float), fbuf, GL_STATIC_DRAW);
+
+ glTexCoordPointer(2, GL_FLOAT, kRowSize, (GLvoid*)(3*4));
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+
+ glVertexPointer(3, GL_FLOAT, kRowSize, 0);
+ glEnableClientState(GL_VERTEX_ARRAY);
+
+ glDrawArrays(GL_TRIANGLES, 0, 6);
+
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glDisableClientState(GL_VERTEX_ARRAY);
+
+ // END
+
+ SDL_GL_SwapBuffers();
+
+ verify();
+
+#if !EMSCRIPTEN
+ SDL_Delay(1500);
+#endif
+
+ SDL_Quit();
+
+ return 0;
+}
diff --git a/tests/runner.py b/tests/runner.py
index 49991498..31408b96 100755
--- a/tests/runner.py
+++ b/tests/runner.py
@@ -259,12 +259,14 @@ process(sys.argv[1])
def run_native(self, filename, args):
Popen([filename+'.native'] + args, stdout=PIPE).communicate()[0]
- def assertIdentical(self, x, y):
- if x != y:
- raise Exception("Expected to have '%s' == '%s', diff:\n\n%s" % (
- limit_size(x), limit_size(y),
- limit_size(''.join([a.rstrip()+'\n' for a in difflib.unified_diff(x.split('\n'), y.split('\n'), fromfile='expected', tofile='actual')]))
- ))
+ def assertIdentical(self, values, y):
+ if type(values) not in [list, tuple]: values = [values]
+ for x in values:
+ if x == y: return # success
+ raise Exception("Expected to have '%s' == '%s', diff:\n\n%s" % (
+ limit_size(values[0]), limit_size(y),
+ limit_size(''.join([a.rstrip()+'\n' for a in difflib.unified_diff(x.split('\n'), y.split('\n'), fromfile='expected', tofile='actual')]))
+ ))
def assertContained(self, values, string, additional_info=''):
if type(values) not in [list, tuple]: values = [values]
@@ -4023,8 +4025,9 @@ def process(filename):
var Module = {
'noFSInit': true,
'preRun': function() {
- FS.createDataFile('/', 'somefile.binary', [100, 200, 50, 25, 10, 77, 123], true, false); // 200 becomes -56, since signed chars are used in memory
FS.createLazyFile('/', 'test.file', 'test.file', true, false);
+ // Test FS_* exporting
+ Module['FS_createDataFile']('/', 'somefile.binary', [100, 200, 50, 25, 10, 77, 123], true, false); // 200 becomes -56, since signed chars are used in memory
var test_files_input = 'hi there!';
var test_files_input_index = 0;
FS.init(function() {
@@ -6836,6 +6839,10 @@ f.close()
Popen(['python', EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--embed-file', 'somefile.txt']).communicate()
self.assertContained('|hello from a file wi|', run_js(os.path.join(self.get_dir(), 'a.out.js')))
+ # preload twice, should not err
+ Popen(['python', EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--embed-file', 'somefile.txt', '--embed-file', 'somefile.txt']).communicate()
+ self.assertContained('|hello from a file wi|', run_js(os.path.join(self.get_dir(), 'a.out.js')))
+
def test_multidynamic_link(self):
# Linking the same dynamic library in will error, normally, since we statically link it, causing dupe symbols
# A workaround is to use --ignore-dynamic-linking, see emcc --help for details
@@ -7176,6 +7183,7 @@ elif 'browser' in str(sys.argv):
output = queue.get()
break
time.sleep(0.1)
+
self.assertIdentical(expectedResult, output)
finally:
server.terminate()
@@ -7417,11 +7425,11 @@ elif 'browser' in str(sys.argv):
self.run_browser('page.html', '', '/report_result?1')
def test_sdl_image(self):
- # load an image file, get pixel data
+ # load an image file, get pixel data. Also O2 coverage for --preload-file
shutil.copyfile(path_from_root('tests', 'screenshot.jpg'), os.path.join(self.get_dir(), 'screenshot.jpg'))
open(os.path.join(self.get_dir(), 'sdl_image.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_image.c')).read()))
- Popen(['python', EMCC, os.path.join(self.get_dir(), 'sdl_image.c'), '--preload-file', 'screenshot.jpg', '-o', 'page.html']).communicate()
+ Popen(['python', EMCC, os.path.join(self.get_dir(), 'sdl_image.c'), '-O2', '--preload-file', 'screenshot.jpg', '-o', 'page.html']).communicate()
self.run_browser('page.html', '', '/report_result?600')
def test_sdl_image_compressed(self):
@@ -7548,7 +7556,7 @@ elif 'browser' in str(sys.argv):
# SDL, OpenGL, textures, immediate mode. Closure for more coverage
shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'screenshot.png'))
self.reftest(path_from_root('tests', 'screenshot-gray-purple.png'))
- Popen(['python', EMCC, path_from_root('tests', 'sdl_ogl_defaultMatrixMode.c'), '-O2', '--minify', '0', '-o', 'something.html', '--pre-js', 'reftest.js', '--preload-file', 'screenshot.png']).communicate()
+ Popen(['python', EMCC, path_from_root('tests', 'sdl_ogl_defaultMatrixMode.c'), '--minify', '0', '-o', 'something.html', '--pre-js', 'reftest.js', '--preload-file', 'screenshot.png']).communicate()
self.run_browser('something.html', 'You should see an image with gray at the top.', '/report_result?0')
def test_sdl_ogl_p(self):
@@ -7647,7 +7655,8 @@ elif 'browser' in str(sys.argv):
self.reftest(path_from_root('tests', reference))
args += ['--pre-js', 'reftest.js']
Popen(['python', EMCC, os.path.join(self.get_dir(), filename), '-o', 'test.html'] + args).communicate()
- self.run_browser('test.html', '.', '/report_result?' + expected)
+ if type(expected) is str: expected = [expected]
+ self.run_browser('test.html', '.', ['/report_result?' + e for e in expected])
def test_emscripten_api(self):
self.btest('emscripten_api_browser.cpp', '1')
@@ -7666,6 +7675,9 @@ elif 'browser' in str(sys.argv):
shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'screenshot.png'))
self.btest('gl_ps.c', reference='gl_ps.png', args=['--preload-file', 'screenshot.png'])
+ def test_matrix_identity(self):
+ self.btest('gl_matrix_identity.c', expected=['-1882984448', '460451840'])
+
def test_cubegeom_pre(self):
self.btest('cubegeom_pre.c', expected='-1472804742')
diff --git a/tools/file_packager.py b/tools/file_packager.py
new file mode 100644
index 00000000..43f04d8c
--- /dev/null
+++ b/tools/file_packager.py
@@ -0,0 +1,290 @@
+'''
+A tool that generates FS API calls to generate a filesystem, and packages the files
+to work with that.
+
+This is called by emcc. You can also call it yourself.
+
+Usage:
+
+ file_packager.py TARGET [--preload A [B..]] [--embed C [D..]] [--compress COMPRESSION_DATA] [--pre-run]
+
+'''
+
+import os, sys
+
+from shared import Compression, execute
+
+def suffix(name):
+ return name.split('.')[-1]
+
+IMAGE_SUFFIXES = ('.jpg', '.png', '.bmp')