From d57c2300656c5ea399024f641a8c42e996c25b57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jukka=20Jyl=C3=A4nki?= Date: Wed, 7 Jun 2017 14:56:23 +0300 Subject: [PATCH 01/16] Fix support for passing 32-bit unsigned integers through with embind in wasm build mode. Fixes #5277 for 32-bit case. --- src/embind/embind.js | 4 +++- tests/embind/test_unsigned.cpp | 36 ++++++++++++++++++++++++++++++++++ tests/embind/test_unsigned.out | 3 +++ tests/test_core.py | 4 ++++ 4 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 tests/embind/test_unsigned.cpp create mode 100644 tests/embind/test_unsigned.out diff --git a/src/embind/embind.js b/src/embind/embind.js index b096de93c42d7..b8481176fde6e 100644 --- a/src/embind/embind.js +++ b/src/embind/embind.js @@ -536,6 +536,8 @@ var LibraryEmbind = { }; } + var isUnsignedType = (name.indexOf('unsigned') != -1); + registerType(primitiveType, { name: name, 'fromWireType': fromWireType, @@ -548,7 +550,7 @@ var LibraryEmbind = { if (value < minRange || value > maxRange) { throw new TypeError('Passing a number "' + _embind_repr(value) + '" from JS side to C/C++ side to an argument of type "' + name + '", which is outside the valid range [' + minRange + ', ' + maxRange + ']!'); } - return value | 0; + return isUnsignedType ? (value >>> 0) : (value | 0); }, 'argPackAdvance': 8, 'readValueFromPointer': integerReadValueFromPointer(name, shift, minRange !== 0), diff --git a/tests/embind/test_unsigned.cpp b/tests/embind/test_unsigned.cpp new file mode 100644 index 0000000000000..29b7f58998bd9 --- /dev/null +++ b/tests/embind/test_unsigned.cpp @@ -0,0 +1,36 @@ +#include +#include +#include +static void set_bind_f64(emscripten::val val) { + printf("set_bind_f64: %x\n", (uint32_t)val.as()); +} +static void set_bind_u64(emscripten::val val) { + printf("set_bind_u64: %x\n", (uint32_t)val.as()); +} +static void set_bind_u32(emscripten::val val) { + printf("set_bind_u32: %x\n", val.as()); +} +extern "C" { + EMSCRIPTEN_KEEPALIVE void set_c_u64(uint64_t v) { + printf("set_c_u64: %x\n", (uint32_t)v); + } + EMSCRIPTEN_KEEPALIVE void set_c_u32(uint32_t v) { + printf("set_c_u32: %x\n", v); + } +} +EMSCRIPTEN_BINDINGS(TEST) { + emscripten::function("set_bind_f64", &set_bind_f64); + emscripten::function("set_bind_u64", &set_bind_u64); + emscripten::function("set_bind_u32", &set_bind_u32); +} + +int main() +{ + EM_ASM( + Module['set_bind_f64'](2147483648); +// Module['set_bind_u64'](2147483648); // todo: embind does not currently support 64-bit integers. + Module['set_bind_u32'](2147483648); +// Module['_set_c_u64'](2147483648); // todo: embind does not currently support 64-bit integers. + Module['_set_c_u32'](2147483648); + ); +} diff --git a/tests/embind/test_unsigned.out b/tests/embind/test_unsigned.out new file mode 100644 index 0000000000000..2ff5b58cc87c0 --- /dev/null +++ b/tests/embind/test_unsigned.out @@ -0,0 +1,3 @@ +set_bind_f64: 80000000 +set_bind_u32: 80000000 +set_c_u32: 80000000 diff --git a/tests/test_core.py b/tests/test_core.py index 99439fe649b2f..a410693136ab8 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -6337,6 +6337,10 @@ def test_embind_5(self): Building.COMPILER_TEST_OPTS += ['--bind'] self.do_run_in_out_file_test('tests', 'core', 'test_embind_5') + def test_embind_unsigned(self): + self.emcc_args += ['--bind', '--std=c++11'] + self.do_run_from_file(path_from_root('tests', 'embind', 'test_unsigned.cpp'), path_from_root('tests', 'embind', 'test_unsigned.out')) + @sync @no_wasm_backend() def test_scriptaclass(self): From a5a596d11273d704f46cf4d2c504c476b271d136 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jukka=20Jyl=C3=A4nki?= Date: Tue, 13 Jun 2017 14:23:21 +0300 Subject: [PATCH 02/16] Document WebGL 2/GLES 3 linker flags. #5293 --- .../OpenGL-support.rst | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/site/source/docs/porting/multimedia_and_graphics/OpenGL-support.rst b/site/source/docs/porting/multimedia_and_graphics/OpenGL-support.rst index 95ece43ea7d9b..28e48ea936e16 100644 --- a/site/source/docs/porting/multimedia_and_graphics/OpenGL-support.rst +++ b/site/source/docs/porting/multimedia_and_graphics/OpenGL-support.rst @@ -6,8 +6,8 @@ OpenGL support in Emscripten Emscripten provides three OpenGL modes: -- :ref:`opengl-support-webgl-subset` (default) — supports the set of OpenGL ES commands that map directly to WebGL. -- :ref:`opengl-support-opengl-es2-0-emulation` — support for some emulated OpenGL ES 2.0 features that are not present in WebGL. +- :ref:`opengl-support-webgl-subset` (default) — supports the set of OpenGL ES 2.0/3.0 commands that map directly to WebGL 1/2. +- :ref:`opengl-support-opengl-es2-0-emulation` — support for some emulated OpenGL ES 2.0/3.0 features that are not present in WebGL. - :ref:`opengl-support-legacy_and_mobile` — support for a number of legacy GL 1.x features and commands. This topic provides information about the modes, and how they are enabled. @@ -16,8 +16,8 @@ This topic provides information about the modes, and how they are enabled. .. _opengl-support-webgl-subset: -WebGL-friendly subset of OpenGL ES -================================== +WebGL-friendly subset of OpenGL ES 2.0/3.0 +========================================== By default, Emscripten targets the WebGL-friendly subset of OpenGL ES 2.0. This is the set of GL ES commands that map directly to WebGL, so that each GL command has a roughly direct mapping to WebGL. It includes almost all of OpenGL ES 2.0, with the notable exception of client-side arrays, and some other features that are listed in `WebGL 1.0 Specification/Chapter 6 `_. @@ -25,12 +25,14 @@ To program against the WebGL subset of OpenGL ES, one uses the GL ES 2.0 header This mode is used by default because it best matches the WebGL features brovided by browsers. +To target WebGL 2, pass the linker flag ``-s USE_WEBGL2=1``. Specifying this flag enables (and defaults to, unless otherwise specified at context creation time) the creation of WebGL 2 contexts at runtime, but it is still possible to create WebGL 1 contexts, so applications can choose whether to require WebGL 2 or whether to support a fallback to WebGL 1. + .. _opengl-support-opengl-es2-0-emulation: -OpenGL ES 2.0 emulation -======================= +OpenGL ES 2.0/3.0 emulation +=========================== -This build mode emulates some features of OpenGL ES 2.0 that are not part of the core WebGL 1 specification. +This build mode emulates some features of OpenGL ES 2.0/3.0 that are not part of the core WebGL 1 specification. In particular, this mode emulates client-side arrays that are missing [#f1]_ from the :ref:`opengl-support-webgl-subset`. @@ -40,6 +42,8 @@ This allows you to use functions `glDrawArrays ` option ``-s FULL_ES2=1`` when linking the final executable (.js/.html) of the project. +To enable *OpenGL ES 3.0 emulation*, specify the :ref:`emcc ` option ``-s FULL_ES3=1`` when linking the final executable (.js/.html) of the project. This adds emulation for mapping memory blocks to client side memory. + .. _opengl-support-legacy_and_mobile: Emulation of older Desktop OpenGL API features @@ -72,6 +76,8 @@ When porting code, it should be noted that desktop OpenGL, OpenGL ES and WebGL e Additionally, in WebGL, unlike in desktop or mobile OpenGL, extensions must be activated first before the features they expose take effect. If you use one of the native APIs SDL, EGL, GLUT or GLFW to create your GL context, this will be done automatically for most extensions. If instead you use the HTML5 WebGL context creation API, you must explicitly choose whether to autoenable WebGL extensions. If an extension was not automatically enabled at context creation time, the HTML5 API function `emscripten_webgl_enable_extension` can be used to activate it. Debugging related extensions, draft extensions and vendor-prefixed extensions (MOZ_*, WEBKIT_*) are never enabled automatically at context creation time, but must always be activated manually. +When migrating from WebGL 1 to WebGL 2, take note that some WebGL 1 extensions are migrated to core WebGL 2, and therefore their functionality is no longer advertised as GL extensions. This does not mean that the features would be missing, but that it is possible to utilize these features in WebGL 2 without needing to feature test the presence of a GL extension first. + Test code/examples ================== From abb142b18cf3481b3702cdbade0d98019adb4e42 Mon Sep 17 00:00:00 2001 From: Aleksander Guryanov Date: Wed, 14 Jun 2017 00:25:37 +0700 Subject: [PATCH 03/16] Fix cutting when render text through TTF_RenderText_Solid (#5269) --- src/library_sdl.js | 9 ++++++--- tests/sdl_ttf_render_text_solid.c | 19 +++++++++++++++++++ tests/sdl_ttf_render_text_solid.png | Bin 0 -> 1554 bytes tests/test_browser.py | 3 +++ 4 files changed, 28 insertions(+), 3 deletions(-) create mode 100644 tests/sdl_ttf_render_text_solid.c create mode 100644 tests/sdl_ttf_render_text_solid.png diff --git a/src/library_sdl.js b/src/library_sdl.js index 96851be1befbd..9e17166dc3d99 100644 --- a/src/library_sdl.js +++ b/src/library_sdl.js @@ -3001,14 +3001,17 @@ var LibrarySDL = { var w = SDL.estimateTextWidth(fontData, text); var h = fontData.size; var color = SDL.loadColorToCSSRGB(color); // XXX alpha breaks fonts? - var fontString = h + 'px ' + fontData.name; + var fontString = h + 'px ' + fontData.name + ', serif'; var surf = SDL.makeSurface(w, h, 0, false, 'text:' + text); // bogus numbers.. var surfData = SDL.surfaces[surf]; surfData.ctx.save(); surfData.ctx.fillStyle = color; surfData.ctx.font = fontString; - surfData.ctx.textBaseline = 'top'; - surfData.ctx.fillText(text, 0, 0); + // use bottom alligment, because it works + // same in all browsers, more info here: + // https://bugzilla.mozilla.org/show_bug.cgi?id=737852 + surfData.ctx.textBaseline = 'bottom'; + surfData.ctx.fillText(text, 0, h|0); surfData.ctx.restore(); return surf; }, diff --git a/tests/sdl_ttf_render_text_solid.c b/tests/sdl_ttf_render_text_solid.c new file mode 100644 index 0000000000000..05fadfe33f74b --- /dev/null +++ b/tests/sdl_ttf_render_text_solid.c @@ -0,0 +1,19 @@ +#include +#include +#include + +int main() +{ + SDL_Init(SDL_INIT_VIDEO); + SDL_Surface *screen = SDL_SetVideoMode(320, 32, 32, SDL_HWSURFACE); + + TTF_Init(); + TTF_Font *font = TTF_OpenFont("Arial", 32); + + SDL_Color color = { 0xff, 0x99, 0x00, 0xff }; + SDL_Surface *text = TTF_RenderText_Solid(font, "Play", color); + SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 255, 0, 0)); + SDL_BlitSurface(text, NULL, screen, NULL); + return 0; +} + diff --git a/tests/sdl_ttf_render_text_solid.png b/tests/sdl_ttf_render_text_solid.png new file mode 100644 index 0000000000000000000000000000000000000000..f94ac8f086248ad978cf661803cf77a886a367b7 GIT binary patch literal 1554 zcmV+t2JQKYP)a&SB+~(`;|k6TI#2cxV2C%Vj$EcZQ#;Q*~-)%!b*-LLdY}U;+uuhyYIDsOObK zAO!v)Fh>CQ1a=-kMPKm`;AdbK@GkHQ@O6*#4hJ?V`z{8)4fPNRfe`rr5SSx?GlAWw z@L<0KPXG_MoO>*=McICOAEBJWZF5Qpgg^*%CD1N_dTQVFxb#B662S6}{f`3=Hnv3o zyN*xJ4}lPvhXmRM@R1%KUHw5;1rDl(xPJkLxu4b!iU7{Tq06O1AOyM+n7ROZ=Y@eY zfi=owJOkVl0qi;`IX?tKVEz#3CV)QUj=&-1VfFqli~!D`q03!DAOyM+=q`Zk1E-b8 z_yIU+3Iezeur06=uw0pA{0;aX_z-vrc)RhcO9K0pZDyq(7=U61a6s`|z3xL@(dGOQ z2!ZKNpt}HW2%J(dSPo;hx3WQVfQtZ!0ox3LQ4zcYaIUekbAdI>2j?F@?sJ`uYvTMg zaPJWJ%poBV0%J{}y8v20xOZXn7I59a5WwSs&C2m+o)x(-fIo{TxH+&v?RxhEPu4!~ zQoK4hx!wJ}>`-8bvhyO~ld&Gxyj%!`zz_tw3E(Ql<1-x9?gpM6QUJFEjw#3Lxm^YP z((=T+0sEHip8*%tj#(PG99XcF?dW~BE$lN{b0x5N@e=hGV<8X%6HlOB03QV`g=yLX zz@os)fZmxNn4+Q?{u+`xcUM`z5csGv@8_{s1Ml=03-75oDzb*Zie_Epjs~_W=jwgx zg*I(F;K;JkEVbF{SO|o`#1rVg5=4ZG-u1wH4W9i3(9)UJfE57eXXXqp5zO_oh@T}ToG(TYZEpmL1G~g0uxW5y8!CB=~cDOsSQek48m`SYi9VsGAHQq z&Kv$(J5dkOFnE~~LZ93aNVmJ+<6Z-98FbU^4}lPv6awu6Xd=SDrWt}NK0lQeA9{RW z_CYnQ04`OM9eRX@*DC?5*F>*=i>3g&Z+~Fda?tH1nNpooPn0NrieM}RLLda@>IPG9 zhA_-hxMA=O5)(fw3phEekvdPd@>)e$S%mDue8`6e%;kpBE4FgA!VwQJVv76F|@PnDvUlDRf@T zdzhHfn#=SCJ$v?tKnRRIfzb)zS%51*G&8r>@b#+Avu@9l8?BcB?gSiMj=2kXzN{44 zqih>gC_VN=ofiy&5NHw@jR39%oKqgfwp)g&^~`SlDNSBn3oKSPp3tLS&=y@+l%;NO z0ymVf*zj0kRYYSU5CS1kBQP2Pv~JJ(J?(Yi=00O$LsdiR%1#7+>~Xxce%AciwffSU z$leD$m2nS&5D0;}35-Sn>+5pADnezAUelH>i3^h@m08Evt6A%&fF7due#tDS_AlT0 z5D0-D1V$r(E={`(u>3`H4Y17#zAK+yZDNB2yS*rabvwT#N!nh>jxTzC^={RJJj{GG zLLdYtmcVEPP~6)CN7T5x5=76=|F*<(?oWHM`~cHl<#V&nuQ_x!X0Sm+Ww!#aPVD&R z#X}$j`VbhM0QykQ1zN;x@pIi2K||Qb023P4@tG*mnst87mor;jB|I#JF=Td5FctzK z5CRPXqyJrC!&Wo33YVd=qGflOsjk;G{51^rx~cuk7d`|+U?>l8TrgSpS9OX`?ccbJ zYThFR<_Uq(dw~DtYx$F%_6sy?Y%6^eEwK;?fe`3HV0sIni4sFzlP4>d#;IGEH@yvb zIpZDzArJz+2~2MRbe`{~mPQJ8sj-P_zaE46R)jzZOgw?B~zyJb&17sU!@U7&(C;$Ke07*qoM6N<$ Eg4m(JmjD0& literal 0 HcmV?d00001 diff --git a/tests/test_browser.py b/tests/test_browser.py index a32d90d916ae9..3d862421f4257 100644 --- a/tests/test_browser.py +++ b/tests/test_browser.py @@ -1872,6 +1872,9 @@ def test_sdl_canvas_palette_2(self): self.btest('sdl_canvas_palette_2.c', reference='sdl_canvas_palette_g.png', args=['--pre-js', 'pre.js', '--pre-js', 'args-g.js', '-lSDL', '-lGL']) self.btest('sdl_canvas_palette_2.c', reference='sdl_canvas_palette_b.png', args=['--pre-js', 'pre.js', '--pre-js', 'args-b.js', '-lSDL', '-lGL']) + def test_sdl_ttf_render_text_solid(self): + self.btest('sdl_ttf_render_text_solid.c', reference='sdl_ttf_render_text_solid.png', args=['-O2', '-s', 'TOTAL_MEMORY=16MB', '-lSDL', '-lGL']) + def test_sdl_alloctext(self): self.btest('sdl_alloctext.c', expected='1', args=['-O2', '-s', 'TOTAL_MEMORY=16MB', '-lSDL', '-lGL']) From 72c54eaa0f6889d363bd76c3593dfdf5f0a48630 Mon Sep 17 00:00:00 2001 From: Vladimir Davidovich Date: Tue, 13 Jun 2017 20:28:30 +0300 Subject: [PATCH 04/16] include credentials when fetching wasm binaries (#5287) * include credentials when fetching wasm binaries Fetch API does not include cookies/credentials by default as XHR would. To cause browsers to send a request with credentials included, add credentials: 'include' to the init object you pass to the fetch() method. * use { credentials: 'same-origin' } here to have same behavior as XMLHttpRequest would, since we never used xhr.withCredentials = true --- src/preamble.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/preamble.js b/src/preamble.js index e3521d04076de..87511b3c5f1fa 100644 --- a/src/preamble.js +++ b/src/preamble.js @@ -1981,7 +1981,7 @@ addOnPreRun(function() { addRunDependency('preload_dynamicLibraries'); var binaries = []; Module['dynamicLibraries'].forEach(function(lib) { - fetch(lib).then(function(response) { + fetch(lib, { credentials: 'same-origin' }).then(function(response) { if (!response['ok']) { throw "failed to load wasm binary file at '" + lib + "'"; } @@ -2226,7 +2226,7 @@ function integrateWasmJS(Module) { function getBinaryPromise() { // if we don't have the binary yet, and have the Fetch api, use that if (!Module['wasmBinary'] && typeof fetch === 'function') { - return fetch(wasmBinaryFile).then(function(response) { + return fetch(wasmBinaryFile, { credentials: 'same-origin' }).then(function(response) { if (!response['ok']) { throw "failed to load wasm binary file at '" + wasmBinaryFile + "'"; } From 2095a88a25cc5c2c773a302f2b51a00158e1f51a Mon Sep 17 00:00:00 2001 From: Jukka Jylanki Date: Wed, 14 Jun 2017 10:50:15 +0300 Subject: [PATCH 05/16] Fix issues where Emscripten toolchain would not work if installed to a directory that contains unicode characters. --- emscripten.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/emscripten.py b/emscripten.py index 4dec8d80e14b9..2eebf220b8e70 100755 --- a/emscripten.py +++ b/emscripten.py @@ -2076,7 +2076,10 @@ def main(args, compiler_engine, cache, temp_files, DEBUG): settings = {} for setting in args.settings: name, value = setting.strip().split('=', 1) - settings[name] = json.loads(value) + value = json.loads(value) + if isinstance(value, unicode): + value = value.encode('utf8') + settings[name] = value # libraries libraries = args.libraries[0].split(',') if len(args.libraries) > 0 else [] From f96ee252f80caa00327766bf4c9cf1cf78598658 Mon Sep 17 00:00:00 2001 From: Jukka Jylanki Date: Wed, 14 Jun 2017 11:51:05 +0300 Subject: [PATCH 06/16] Fix test_cube2hash, test_lua and test_zlib when Emscripten is located in a directory that contains spaces. --- tools/shared.py | 50 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 35 insertions(+), 15 deletions(-) diff --git a/tools/shared.py b/tools/shared.py index e56cb31897a04..e52332258ec38 100644 --- a/tools/shared.py +++ b/tools/shared.py @@ -1358,13 +1358,32 @@ def close_multiprocessing_pool(): return Building.multiprocessing_pool + # When creating environment variables for Makefiles to execute, we need to doublequote the commands if they have spaces in them.. @staticmethod - def get_building_env(native=False): + def doublequote_spaces(arg): + if not (arg.startswith('"') and arg.endswith('"')): + return '"' + arg.replace('"', '\\"') + '"' + else: + return + + # .. but for Popen, we cannot have doublequotes, so provide functionality to remove them when needed. + @staticmethod + def remove_doublequotes(arg): + if arg.startswith('"') and arg.endswith('"'): + return arg[1:-1].replace('\\"', '"') + else: + return arg + + @staticmethod + def get_building_env(native=False, doublequote_commands=False): + def nop(arg): + return arg + quote = Building.doublequote_spaces if doublequote_commands else nop env = os.environ.copy() if native: - env['CC'] = CLANG_CC - env['CXX'] = CLANG_CPP - env['LD'] = CLANG + env['CC'] = quote(CLANG_CC) + env['CXX'] = quote(CLANG_CPP) + env['LD'] = quote(CLANG) env['CFLAGS'] = '-O2 -fno-math-errno' # get a non-native one, and see if we have some of its effects - remove them if so non_native = Building.get_building_env() @@ -1374,18 +1393,18 @@ def get_building_env(native=False): if env.get(dangerous) and env.get(dangerous) == non_native.get(dangerous): del env[dangerous] # better to delete it than leave it, as the non-native one is definitely wrong return env - env['CC'] = EMCC if not WINDOWS else 'python %r' % EMCC - env['CXX'] = EMXX if not WINDOWS else 'python %r' % EMXX - env['AR'] = EMAR if not WINDOWS else 'python %r' % EMAR - env['LD'] = EMCC if not WINDOWS else 'python %r' % EMCC - env['NM'] = LLVM_NM - env['LDSHARED'] = EMCC if not WINDOWS else 'python %r' % EMCC - env['RANLIB'] = EMRANLIB if not WINDOWS else 'python %r' % EMRANLIB - env['EMMAKEN_COMPILER'] = Building.COMPILER + env['CC'] = quote(EMCC if not WINDOWS else 'python %r' % EMCC) + env['CXX'] = quote(EMXX if not WINDOWS else 'python %r' % EMXX) + env['AR'] = quote(EMAR if not WINDOWS else 'python %r' % EMAR) + env['LD'] = quote(EMCC if not WINDOWS else 'python %r' % EMCC) + env['NM'] = quote(LLVM_NM) + env['LDSHARED'] = quote(EMCC if not WINDOWS else 'python %r' % EMCC) + env['RANLIB'] = quote(EMRANLIB if not WINDOWS else 'python %r' % EMRANLIB) + env['EMMAKEN_COMPILER'] = quote(Building.COMPILER) env['EMSCRIPTEN_TOOLS'] = path_from_root('tools') env['CFLAGS'] = env['EMMAKEN_CFLAGS'] = ' '.join(Building.COMPILER_TEST_OPTS) - env['HOST_CC'] = CLANG_CC - env['HOST_CXX'] = CLANG_CPP + env['HOST_CC'] = quote(CLANG_CC) + env['HOST_CXX'] = quote(CLANG_CPP) env['HOST_CFLAGS'] = "-W" #if set to nothing, CFLAGS is used, which we don't want env['HOST_CXXFLAGS'] = "-W" #if set to nothing, CXXFLAGS is used, which we don't want env['PKG_CONFIG_LIBDIR'] = path_from_root('system', 'local', 'lib', 'pkgconfig') + os.path.pathsep + path_from_root('system', 'lib', 'pkgconfig') @@ -1556,7 +1575,7 @@ def build_library(name, build_dir, output_dir, generated_libs, configure=['sh', # os.unlink(lib) # make sure compilation completed successfully # except: # pass - env = Building.get_building_env(native) + env = Building.get_building_env(native, True) for k, v in env_init.iteritems(): env[k] = v if configure: # Useful in debugging sometimes to comment this out (and the lines below up to and including the |link| call) @@ -2536,6 +2555,7 @@ def make_shared_library(js_file, wasm_file): def execute(cmd, *args, **kw): try: + cmd[0] = Building.remove_doublequotes(cmd[0]) return Popen(cmd, *args, **kw).communicate() # let compiler frontend print directly, so colors are saved (PIPE kills that) except: if not isinstance(cmd, str): From a735254602867933fdce0ff0a547113e597e832e Mon Sep 17 00:00:00 2001 From: Jukka Jylanki Date: Wed, 14 Jun 2017 12:13:42 +0300 Subject: [PATCH 07/16] Fix bug in test runner where tests using --js-transform would not run if Emscripten was installed in a directory containing spaces. --- tests/runner.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/runner.py b/tests/runner.py index a3aa2c4439508..bd4439dfc9bf9 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -282,7 +282,7 @@ def ll_to_js(self, filename, extra_emscripten_args, post_build): transform.write(post1) transform.write('\nprocess(sys.argv[1])\n') transform.close() - transform_args = ['--js-transform', "%s %s" % (PYTHON, transform_filename)] + transform_args = ['--js-transform', "%s '%s'" % (PYTHON, transform_filename)] Building.emcc(filename + '.o', Settings.serialize() + emcc_args + transform_args + Building.COMPILER_TEST_OPTS, filename + '.o.js') if post2: post2(filename + '.o.js') From 766ddc7a01ae5e95c0d82957d58613b63375ff10 Mon Sep 17 00:00:00 2001 From: Jukka Jylanki Date: Wed, 14 Jun 2017 13:48:36 +0300 Subject: [PATCH 08/16] Fix emcc --cflags, other.test_emcc_cflags, other.test_stdin and test_source_map when Emscripten is installed to a directory that contains spaces. --- emcc.py | 9 ++++----- tests/test_core.py | 21 ++++++++++++++++++--- tests/test_other.py | 8 ++++---- tools/shared.py | 13 ++++++++++--- 4 files changed, 36 insertions(+), 15 deletions(-) diff --git a/emcc.py b/emcc.py index 654d26cda16f1..7bd9d3cd0aae3 100755 --- a/emcc.py +++ b/emcc.py @@ -373,11 +373,10 @@ def run(): input_file = 'hello_world.c' out, err = subprocess.Popen([shared.PYTHON] + args + [shared.path_from_root('tests', input_file), '-c', '-o', temp_target], stderr=subprocess.PIPE, env=debug_env).communicate() lines = filter(lambda x: shared.CLANG_CC in x and input_file in x, err.split(os.linesep)) - line = lines[0] - assert 'running:' in line - parts = line.split(' ')[2:] + line = re.search('running: (.*)', lines[0]).group(1) + parts = shlex.split(line) parts = filter(lambda x: x != '-c' and x != '-o' and input_file not in x and temp_target not in x and '-emit-llvm' not in x, parts) - print ' '.join(parts) + print ' '.join(shared.Building.doublequote_spaces(parts)) exit(0) def is_minus_s_for_emcc(newargs, i): @@ -1291,7 +1290,7 @@ def compile_source_file(i, input_file): output_file = get_bitcode_file(input_file) temp_files.append((i, output_file)) args = get_bitcode_args([input_file]) + ['-emit-llvm', '-c', '-o', output_file] - logging.debug("running: " + ' '.join(args)) + logging.debug("running: " + ' '.join(shared.Building.doublequote_spaces(args))) # NOTE: Printing this line here in this specific format is important, it is parsed to implement the "emcc --cflags" command execute(args) # let compiler frontend print directly, so colors are saved (PIPE kills that) if not os.path.exists(output_file): logging.error('compiler frontend failed to generate LLVM bitcode, halting') diff --git a/tests/test_core.py b/tests/test_core.py index 99439fe649b2f..f8952e1d9a158 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -6800,14 +6800,29 @@ def build_and_check(): # objects when generating source maps, so we want to make sure the # optimizer can deal with both types. map_filename = out_filename + '.map' - data = json.load(open(map_filename, 'r')) + + def encode_utf8(data): + if isinstance(data, dict): + for key in data: + data[key] = encode_utf8(data[key]) + return data + elif isinstance(data, list): + for i in xrange(len(data)): + data[i] = encode_utf8(data[i]) + return data + elif isinstance(data, unicode): + return data.encode('utf8') + else: + return data + + data = encode_utf8(json.load(open(map_filename, 'r'))) self.assertPathsIdentical(out_filename, data['file']) assert len(data['sources']) == 1, data['sources'] self.assertPathsIdentical(src_filename, data['sources'][0]) self.assertTextDataIdentical(src, data['sourcesContent'][0]) - mappings = json.loads(jsrun.run_js( + mappings = encode_utf8(json.loads(jsrun.run_js( path_from_root('tools', 'source-maps', 'sourcemap2json.js'), - tools.shared.NODE_JS, [map_filename])) + tools.shared.NODE_JS, [map_filename]))) seen_lines = set() for m in mappings: self.assertPathsIdentical(src_filename, m['source']) diff --git a/tests/test_other.py b/tests/test_other.py index 8d3e798c0e108..6670172495fd5 100644 --- a/tests/test_other.py +++ b/tests/test_other.py @@ -413,9 +413,9 @@ def test_emcc_cflags(self): with clean_write_access_to_canonical_temp_dir(): # --cflags needs to set EMCC_DEBUG=1, which needs to create canonical temp directory. output = Popen([PYTHON, EMCC, '--cflags'], stdout=PIPE, stderr=PIPE).communicate() flags = output[0].strip() - self.assertContained(' '.join(COMPILER_OPTS), flags) + self.assertContained(' '.join(Building.doublequote_spaces(COMPILER_OPTS)), flags) # check they work - cmd = [CLANG, path_from_root('tests', 'hello_world.cpp')] + flags.split(' ') + ['-c', '-emit-llvm', '-o', 'a.bc'] + cmd = [CLANG, path_from_root('tests', 'hello_world.cpp')] + shlex.split(flags) + ['-c', '-emit-llvm', '-o', 'a.bc'] subprocess.check_call(cmd) subprocess.check_call([PYTHON, EMCC, 'a.bc']) self.assertContained('hello, world!', run_js(self.in_dir('a.out.js'))) @@ -1275,9 +1275,9 @@ def _test(): # (we'd use run_js() normally) try_delete('out.txt') if os.name == 'nt': # windows - os.system('type "in.txt" | {} >out.txt'.format(' '.join(make_js_command(os.path.normpath(exe), engine)))) + os.system('type "in.txt" | {} >out.txt'.format(' '.join(Building.doublequote_spaces(make_js_command(os.path.normpath(exe), engine))))) else: # posix - os.system('cat in.txt | {} > out.txt'.format(' '.join(make_js_command(exe, engine)))) + os.system('cat in.txt | {} > out.txt'.format(' '.join(Building.doublequote_spaces(make_js_command(exe, engine))))) self.assertContained('abcdef\nghijkl\neof', open('out.txt').read()) Building.emcc(path_from_root('tests', 'module', 'test_stdin.c'), output_filename='a.out.js') diff --git a/tools/shared.py b/tools/shared.py index e52332258ec38..58e1bf6e3bbca 100644 --- a/tools/shared.py +++ b/tools/shared.py @@ -1361,14 +1361,21 @@ def close_multiprocessing_pool(): # When creating environment variables for Makefiles to execute, we need to doublequote the commands if they have spaces in them.. @staticmethod def doublequote_spaces(arg): - if not (arg.startswith('"') and arg.endswith('"')): + arg = arg[:] # Operate on a copy of the input string/list + if isinstance(arg, list): + for i in range(len(arg)): + arg[i] = Building.doublequote_spaces(arg[i]) + return arg + + if ' ' in arg and not (arg.startswith('"') and arg.endswith('"')): return '"' + arg.replace('"', '\\"') + '"' - else: - return + + return arg # .. but for Popen, we cannot have doublequotes, so provide functionality to remove them when needed. @staticmethod def remove_doublequotes(arg): + arg = arg[:] # Operate on a copy of the input string/list if arg.startswith('"') and arg.endswith('"'): return arg[1:-1].replace('\\"', '"') else: From f8ef3da7b7c1f95acda56631973d6c5ce592c6ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jukka=20Jyl=C3=A4nki?= Date: Wed, 14 Jun 2017 17:48:47 +0300 Subject: [PATCH 09/16] If LLVM opt call fails, heuristically detect if it might have been due to passing natively compiled input files to Emscripten. #5305 --- tools/shared.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/tools/shared.py b/tools/shared.py index e56cb31897a04..31bd4fea3b2e1 100644 --- a/tools/shared.py +++ b/tools/shared.py @@ -1834,8 +1834,16 @@ def llvm_opt(filename, opts, out=None): logging.debug('emcc: LLVM opts: ' + ' '.join(opts) + ' [num inputs: ' + str(len(inputs)) + ']') target = out or (filename + '.opt.bc') - output = Popen([LLVM_OPT] + inputs + opts + ['-o', target], stdout=PIPE).communicate()[0] - assert os.path.exists(target), 'Failed to run llvm optimizations: ' + output + proc = Popen([LLVM_OPT] + inputs + opts + ['-o', target], stdout=PIPE) + output = proc.communicate()[0] + if proc.returncode != 0 or not os.path.exists(target): + logging.error('Failed to run llvm optimizations: ' + output) + for i in inputs: + if not os.path.exists(i): + logging.warning('Note: Input file "' + i + '" did not exist.') + elif not Building.is_bitcode(i): + logging.warning('Note: Input file "' + i + '" exists but was not an LLVM bitcode file suitable for Emscripten. Perhaps accidentally mixing native built object files with Emscripten?') + sys.exit(1) if not out: shutil.move(filename + '.opt.bc', filename) return target From a33306ad35ff6d409aa357bd63deeda43775f2cc Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 14 Jun 2017 14:33:10 -0700 Subject: [PATCH 10/16] Fix malloc failure handling (#5289) * fix sbrk, if allocations fails we must undo our effects before doing any operation that might use memory * undo TOTAL_MEMORY properly in that case as well * also fix asm.js memory growth in mixed asm/wasm builds --- src/library.js | 2 +- src/preamble.js | 22 +++++++++++++- tests/core/test_memorygrowth_3.c | 47 ++++++++++++++++++++++++++++++ tests/core/test_memorygrowth_3.txt | 1 + tests/test_core.py | 9 ++++++ 5 files changed, 79 insertions(+), 2 deletions(-) create mode 100644 tests/core/test_memorygrowth_3.c create mode 100644 tests/core/test_memorygrowth_3.txt diff --git a/src/library.js b/src/library.js index a315431021d88..7199f28176feb 100644 --- a/src/library.js +++ b/src/library.js @@ -496,8 +496,8 @@ LibraryManager.library = { totalMemory = getTotalMemory()|0; if ((newDynamicTop|0) > (totalMemory|0)) { if ((enlargeMemory()|0) == 0) { - ___setErrNo({{{ cDefine('ENOMEM') }}}); HEAP32[DYNAMICTOP_PTR>>2] = oldDynamicTop; + ___setErrNo({{{ cDefine('ENOMEM') }}}); return -1; } } diff --git a/src/preamble.js b/src/preamble.js index 87511b3c5f1fa..b58c598393064 100644 --- a/src/preamble.js +++ b/src/preamble.js @@ -1111,6 +1111,8 @@ function enlargeMemory() { Module.printErr('Expected to get back a buffer of size ' + TOTAL_MEMORY + ' bytes, but instead got back a buffer of size ' + replacement.byteLength); } #endif + // restore the state to before this call, we failed + TOTAL_MEMORY = OLD_TOTAL_MEMORY; return false; } @@ -2401,12 +2403,16 @@ function integrateWasmJS(Module) { Module['asmPreload'] = Module['asm']; // Memory growth integration code - Module['reallocBuffer'] = function(size) { + + var asmjsReallocBuffer = Module['reallocBuffer']; + + var wasmReallocBuffer = function(size) { var PAGE_MULTIPLE = Module["usingWasm"] ? WASM_PAGE_SIZE : ASMJS_PAGE_SIZE; // In wasm, heap size must be a multiple of 64KB. In asm.js, they need to be multiples of 16MB. size = alignUp(size, PAGE_MULTIPLE); // round up to wasm page size var old = Module['buffer']; var oldSize = old.byteLength; if (Module["usingWasm"]) { + // native wasm support try { var result = Module['wasmMemory'].grow((size - oldSize) / wasmPageSize); // .grow() takes a delta compared to the previous size if (result !== (-1 | 0)) { @@ -2422,12 +2428,24 @@ function integrateWasmJS(Module) { return null; } } else { + // wasm interpreter support exports['__growWasmMemory']((size - oldSize) / wasmPageSize); // tiny wasm method that just does grow_memory // in interpreter, we replace Module.buffer if we allocate return Module['buffer'] !== old ? Module['buffer'] : null; // if it was reallocated, it changed } }; + Module['reallocBuffer'] = function(size) { + if (finalMethod === 'asmjs') { + return asmjsReallocBuffer(size); + } else { + return wasmReallocBuffer(size); + } + }; + + // we may try more than one; this is the final one, that worked and we are using + var finalMethod = ''; + // Provide an "asm.js function" for the application, called to "link" the asm.js module. We instantiate // the wasm module at that time, and it receives imports and provides exports and so forth, the app // doesn't need to care that it is wasm or olyfilled wasm or asm.js. @@ -2472,6 +2490,8 @@ function integrateWasmJS(Module) { Module['printErr']('trying binaryen method: ' + curr); #endif + finalMethod = curr; + if (curr === 'native-wasm') { if (exports = doNativeWasm(global, env, providedBuffer)) break; } else if (curr === 'asmjs') { diff --git a/tests/core/test_memorygrowth_3.c b/tests/core/test_memorygrowth_3.c new file mode 100644 index 0000000000000..04dbc53bee1a2 --- /dev/null +++ b/tests/core/test_memorygrowth_3.c @@ -0,0 +1,47 @@ +#include +#include +#include +#include +#include +#include "emscripten.h" + +int get_TOTAL_MEMORY() { + return EM_ASM_INT_V({ return TOTAL_MEMORY }); +} + +typedef void* voidStar; + +int main(int argc, char **argv) +{ + int totalMemory = get_TOTAL_MEMORY(); + int chunk = 1024*1024; + volatile voidStar alloc; +#ifdef FAIL_REALLOC_BUFFER + EM_ASM({ + Module.seenOurReallocBuffer = false; + Module['reallocBuffer'] = function() { + Module.seenOurReallocBuffer = true; + return null; + }; + }); +#endif + for (int i = 0; i < (totalMemory/chunk)+2; i++) { + // make sure state remains the same if malloc fails + void* sbrk_before = sbrk(0); + alloc = malloc(chunk); + printf("%d : %d\n", i, !!alloc); + if (alloc == NULL) { + assert(sbrk(0) == sbrk_before); + assert(totalMemory == get_TOTAL_MEMORY()); + break; + } + } + assert(alloc == NULL); +#ifdef FAIL_REALLOC_BUFFER + EM_ASM({ + assert(Module.seenOurReallocBuffer, 'our override should have been called'); + }); +#endif + puts("ok."); + return 0; +} diff --git a/tests/core/test_memorygrowth_3.txt b/tests/core/test_memorygrowth_3.txt new file mode 100644 index 0000000000000..90b5016eff5b5 --- /dev/null +++ b/tests/core/test_memorygrowth_3.txt @@ -0,0 +1 @@ +ok. diff --git a/tests/test_core.py b/tests/test_core.py index 99439fe649b2f..b3f37a3d2d3b0 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -1701,6 +1701,15 @@ def test(): test() Settings.SPLIT_MEMORY = 0 + def test_memorygrowth_3(self): + # checks handling of malloc failure properly + self.emcc_args += ['-s', 'ALLOW_MEMORY_GROWTH=0', '-s', 'ABORTING_MALLOC=0', '-s', 'SAFE_HEAP=1'] + self.do_run_in_out_file_test('tests', 'core', 'test_memorygrowth_3') + + def test_memorygrowth_3_force_fail_reallocBuffer(self): + self.emcc_args += ['-s', 'ALLOW_MEMORY_GROWTH=1', '-DFAIL_REALLOC_BUFFER'] + self.do_run_in_out_file_test('tests', 'core', 'test_memorygrowth_3') + def test_ssr(self): # struct self-ref src = ''' #include From d7738f69f2278320ffff31fc0235c069837bfa3e Mon Sep 17 00:00:00 2001 From: Jacob Gravelle Date: Wed, 14 Jun 2017 17:10:12 -0700 Subject: [PATCH 11/16] Triage new test failures for wasm backend --- tests/test_core.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/test_core.py b/tests/test_core.py index b3f37a3d2d3b0..1ed4646e53616 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -4641,6 +4641,7 @@ def test_strcasecmp(self): def test_atomic(self): self.do_run_in_out_file_test('tests', 'core', 'test_atomic') + @no_wasm_backend('wasm has 64bit lockfree atomics') def test_atomic_cxx(self): test_path = path_from_root('tests', 'core', 'test_atomic_cxx') src, output = (test_path + s for s in ('.cpp', '.txt')) @@ -4923,6 +4924,7 @@ def test_mmap_file(self): src = open(path_from_root('tests', 'mmap_file.c')).read() self.do_run(src, '*\n' + s[0:20] + '\n' + s[4096:4096+20] + '\n*\n') + @no_wasm_backend('FixFunctionBitcasts pass invalidates otherwise-ok function pointer casts') def test_cubescript(self): assert 'asm3' in test_modes if self.run_name == 'asm3': @@ -7165,6 +7167,7 @@ def test_coroutine(self): self.do_run(src, '*0-100-1-101-1-102-2-103-3-104-5-105-8-106-13-107-21-108-34-109-*') @no_emterpreter + @no_wasm_backend('EMTERPRETIFY causes JSOptimizer to run, which is disallowed') def test_emterpretify(self): Settings.EMTERPRETIFY = 1 self.do_run_in_out_file_test('tests', 'core', 'test_hello_world') From 59dcf9baab051786304647daf83f82989a3814e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jukka=20Jyl=C3=A4nki?= Date: Thu, 15 Jun 2017 12:55:47 +0300 Subject: [PATCH 12/16] Add test_native_link_error_message to test for invalid natively compiled input files. --- tests/test_other.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/test_other.py b/tests/test_other.py index 8d3e798c0e108..b3afc4f225c74 100644 --- a/tests/test_other.py +++ b/tests/test_other.py @@ -7653,3 +7653,10 @@ def test_error_on_missing_libraries(self): process.communicate() # TODO: TEMPORARY: When -s ERROR_ON_MISSING_LIBRARIES=1 becomes the default, change the following line to expect failure instead of 0. assert process.returncode is 0, '-llsomenonexistingfile is not yet an error in non-strict mode' + + # Tests that if user accidetally attempts to link natively compiled libraries together with Emscripten, that there should be a helpful error message that informs about what happened. + def test_native_link_error_message(self): + Popen([CLANG, '-c', path_from_root('tests', 'hello_world.c'), '-o', 'hello_world.o'] + get_clang_native_args(), env=get_clang_native_env(), stdout=PIPE, stderr=PIPE).communicate() + Popen([LLVM_AR, 'r', 'hello_world.a', 'hello_world.o'], env=get_clang_native_env(), stdout=PIPE, stderr=PIPE).communicate() + out, err = Popen([PYTHON, EMCC, 'hello_world.a', '-o', 'hello_world.js'], stdout=PIPE, stderr=PIPE).communicate() + assert 'exists but was not an LLVM bitcode file suitable for Emscripten. Perhaps accidentally mixing native built object files with Emscripten?' in err From 67d16943fadc46b4553a3b34db55712a3c9fd995 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jukka=20Jyl=C3=A4nki?= Date: Thu, 15 Jun 2017 12:59:52 +0300 Subject: [PATCH 13/16] Document that FULL_ES2=1 and FULL_ES3=1 are orthogonal (but mutually compatible) --- .../docs/porting/multimedia_and_graphics/OpenGL-support.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/source/docs/porting/multimedia_and_graphics/OpenGL-support.rst b/site/source/docs/porting/multimedia_and_graphics/OpenGL-support.rst index 28e48ea936e16..000391236e355 100644 --- a/site/source/docs/porting/multimedia_and_graphics/OpenGL-support.rst +++ b/site/source/docs/porting/multimedia_and_graphics/OpenGL-support.rst @@ -42,7 +42,7 @@ This allows you to use functions `glDrawArrays ` option ``-s FULL_ES2=1`` when linking the final executable (.js/.html) of the project. -To enable *OpenGL ES 3.0 emulation*, specify the :ref:`emcc ` option ``-s FULL_ES3=1`` when linking the final executable (.js/.html) of the project. This adds emulation for mapping memory blocks to client side memory. +To enable *OpenGL ES 3.0 emulation*, specify the :ref:`emcc ` option ``-s FULL_ES3=1`` when linking the final executable (.js/.html) of the project. This adds emulation for mapping memory blocks to client side memory. The flags ``-s FULL_ES2=1`` and ``-s FULL_ES3=1`` are orthogonal, so either one or both can be specified to emulate different features. .. _opengl-support-legacy_and_mobile: From 2baeb9d4eaa1ed875472e97cae03b8cb80e339f2 Mon Sep 17 00:00:00 2001 From: satoshinm Date: Thu, 15 Jun 2017 14:10:51 -0700 Subject: [PATCH 14/16] Fix glfwSetTime() setting incorrect time offset (#5304) * Fix glfwSetTime() setting incorrect time offset * Add test_glfw_time for glfwSetTime() and glfwGetTime() --- src/library_glfw.js | 2 +- tests/test_browser.py | 3 +++ tests/test_glfw_time.c | 31 +++++++++++++++++++++++++++++++ 3 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 tests/test_glfw_time.c diff --git a/src/library_glfw.js b/src/library_glfw.js index fd81f2da59ca5..48bbea88df0fe 100644 --- a/src/library_glfw.js +++ b/src/library_glfw.js @@ -1086,7 +1086,7 @@ var LibraryGLFW = { }, glfwSetTime: function(time) { - GLFW.initialTime = GLFW.getTime() + time; + GLFW.initialTime = GLFW.getTime() - time; }, glfwExtensionSupported: function(extension) { diff --git a/tests/test_browser.py b/tests/test_browser.py index 3d862421f4257..09304a338b607 100644 --- a/tests/test_browser.py +++ b/tests/test_browser.py @@ -1427,6 +1427,9 @@ def test_glfw(self): def test_glfw_minimal(self): self.btest('glfw_minimal.c', '1', args=['-lglfw', '-lGL']) self.btest('glfw_minimal.c', '1', args=['-s', 'USE_GLFW=2', '-lglfw', '-lGL']) + + def test_glfw_time(self): + self.btest('test_glfw_time.c', '1', args=['-s', 'USE_GLFW=3', '-lglfw', '-lGL']) def test_egl(self): open(os.path.join(self.get_dir(), 'test_egl.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'test_egl.c')).read())) diff --git a/tests/test_glfw_time.c b/tests/test_glfw_time.c new file mode 100644 index 0000000000000..a1b03fc7a08ae --- /dev/null +++ b/tests/test_glfw_time.c @@ -0,0 +1,31 @@ +#include +#include + +int result = 0; +int main() { + if (!glfwInit()) { + return -1; + } + + float t = glfwGetTime(); + printf("glfwGetTime() = %f\n", t); + + printf("glfwSetTime(50)\n"); + glfwSetTime(50); + + // Expect time to be slightly greater than what we set + t = glfwGetTime(); + printf("glfwGetTime() = %f\n", t); + + if (t < 50 + 1e-3) { + result = 1; + } + + glfwTerminate(); + +#ifdef REPORT_RESULT + REPORT_RESULT(); +#endif + + return 0; +} From 417910d8389c5794bed04a155585466fb9483028 Mon Sep 17 00:00:00 2001 From: Sam Clegg Date: Thu, 15 Jun 2017 14:23:53 -0700 Subject: [PATCH 15/16] Use new style python classes (#5300) See: https://wiki.python.org/moin/NewClassVsClassicClass --- emcc.py | 11 +++++------ emscripten.py | 2 +- tests/test_benchmark.py | 2 +- tests/test_other.py | 4 ++-- tests/test_sockets.py | 4 ++-- third_party/WebIDL.py | 2 +- tools/cache.py | 2 +- tools/client_mods.py | 4 ++-- tools/fix_closure.py | 2 +- tools/js_optimizer.py | 4 ++-- tools/shared.py | 20 ++++++++++---------- tools/system_libs.py | 6 +++--- tools/tempfiles.py | 4 ++-- tools/toolchain_profiler.py | 10 +++++----- tools/webidl_binder.py | 2 +- 15 files changed, 39 insertions(+), 40 deletions(-) diff --git a/emcc.py b/emcc.py index 7bd9d3cd0aae3..c83917104a1dd 100755 --- a/emcc.py +++ b/emcc.py @@ -94,7 +94,7 @@ final = None -class Intermediate: +class Intermediate(object): counter = 0 def save_intermediate(name=None, suffix='js'): name = os.path.join(shared.get_emscripten_temp_dir(), 'emcc-%d%s.%s' % (Intermediate.counter, '' if name is None else '-' + name, suffix)) @@ -105,7 +105,7 @@ def save_intermediate(name=None, suffix='js'): Intermediate.counter += 1 -class TimeLogger: +class TimeLogger(object): last = time.time() @staticmethod @@ -120,7 +120,7 @@ def log_time(name): TimeLogger.update() -class EmccOptions: +class EmccOptions(object): def __init__(self): self.opt_level = 0 self.debug_level = 0 @@ -166,7 +166,7 @@ def __init__(self): self.output_eol = os.linesep -class JSOptimizer: +class JSOptimizer(object): def __init__(self, target, options, misc_temp_files, js_transform_tempfiles): self.queue = [] self.extra_info = {} @@ -1670,7 +1670,6 @@ def repl(m): ) with ToolchainProfiler.profile_block('js opts'): # It is useful to run several js optimizer passes together, to save on unneeded unparsing/reparsing - if shared.Settings.DEAD_FUNCTIONS: optimizer.queue += ['eliminateDeadFuncs'] optimizer.extra_info['dead_functions'] = shared.Settings.DEAD_FUNCTIONS @@ -2523,7 +2522,7 @@ def system_js_libraries_setting_str(libs, lib_dirs, settings_changes, input_file return 'SYSTEM_JS_LIBRARIES="' + ','.join(libraries) + '"' -class ScriptSource: +class ScriptSource(object): def __init__(self): self.src = None # if set, we have a script to load with a src attribute self.inline = None # if set, we have the contents of a script to write inline in a script diff --git a/emscripten.py b/emscripten.py index 2eebf220b8e70..0d83280c76134 100755 --- a/emscripten.py +++ b/emscripten.py @@ -685,7 +685,7 @@ def unfloat(s): def make_function_tables_defs(implemented_functions, all_implemented, function_table_data, settings, metadata): - class Counter: + class Counter(object): next_bad_item = 0 next_item = 0 pre = [] diff --git a/tests/test_benchmark.py b/tests/test_benchmark.py index a9218bbeee889..1b3a4bfe85fcf 100644 --- a/tests/test_benchmark.py +++ b/tests/test_benchmark.py @@ -18,7 +18,7 @@ IGNORE_COMPILATION = 0 -class Benchmarker: +class Benchmarker(object): def __init__(self, name): self.name = name diff --git a/tests/test_other.py b/tests/test_other.py index 661526430d6f2..ecc705ae11dc2 100644 --- a/tests/test_other.py +++ b/tests/test_other.py @@ -16,7 +16,7 @@ def multiprocess_task(c_file, cache_dir_name): print '------' sys.exit(1 if 'generating system library: libc.bc' in output else 0) -class temp_directory: +class temp_directory(object): def __enter__(self): self.directory = tempfile.mkdtemp(prefix='emsripten_temp_', dir=TEMP_DIR) self.prev_cwd = os.getcwd() @@ -26,7 +26,7 @@ def __exit__(self, type, value, traceback): os.chdir(self.prev_cwd) # On Windows, we can't have CWD in the directory we're deleting try_delete(self.directory) -class clean_write_access_to_canonical_temp_dir: +class clean_write_access_to_canonical_temp_dir(object): def clean_emcc_files_in_temp_dir(self): for x in os.listdir(CANONICAL_TEMP_DIR): if x.startswith('emcc-') or x.startswith('a.out'): diff --git a/tests/test_sockets.py b/tests/test_sockets.py index 66291797af3dd..6d61423658c32 100644 --- a/tests/test_sockets.py +++ b/tests/test_sockets.py @@ -29,7 +29,7 @@ def make_relay_server(port1, port2): proc = Popen([PYTHON, path_from_root('tests', 'sockets', 'socket_relay.py'), str(port1), str(port2)]) return proc -class WebsockifyServerHarness: +class WebsockifyServerHarness(object): def __init__(self, filename, args, listen_port, do_server_check=True): self.processes = [] self.filename = filename @@ -85,7 +85,7 @@ def __exit__(self, *args, **kwargs): clean_processes(self.processes) -class CompiledServerHarness: +class CompiledServerHarness(object): def __init__(self, filename, args, listen_port): self.processes = [] self.filename = filename diff --git a/third_party/WebIDL.py b/third_party/WebIDL.py index f9113004071b4..ec0076786df67 100644 --- a/third_party/WebIDL.py +++ b/third_party/WebIDL.py @@ -3060,7 +3060,7 @@ def addExtendedAttributes(self, attrs): def _getDependentObjects(self): return set([self._returnType] + self._arguments) -class IDLMethodOverload: +class IDLMethodOverload(object): """ A class that represents a single overload of a WebIDL method. This is not quite the same as an element of the "effective overload set" in the spec, diff --git a/tools/cache.py b/tools/cache.py index 3187d9f91d8da..0d28920ab2abe 100644 --- a/tools/cache.py +++ b/tools/cache.py @@ -3,7 +3,7 @@ import tempfiles, filelock # Permanent cache for dlmalloc and stdlibc++ -class Cache: +class Cache(object): # If EM_EXCLUSIVE_CACHE_ACCESS is true, this process is allowed to have direct access to # the Emscripten cache without having to obtain an interprocess lock for it. Generally this diff --git a/tools/client_mods.py b/tools/client_mods.py index 4f779e31d7c64..c0f195ab5d4a8 100644 --- a/tools/client_mods.py +++ b/tools/client_mods.py @@ -1,5 +1,5 @@ -class PreciseF32: +class PreciseF32(object): name = 'PRECISE_F32 == 2' @staticmethod @@ -36,7 +36,7 @@ def get(settings, minified): return ['if (!Math.fround) { ' + mod + ' }'] return [] -class Pthreads: +class Pthreads(object): name = 'USE_PTHREADS == 2' @staticmethod diff --git a/tools/fix_closure.py b/tools/fix_closure.py index 91d2a86674e26..e93eed42a4e7e 100755 --- a/tools/fix_closure.py +++ b/tools/fix_closure.py @@ -15,7 +15,7 @@ infile = open(sys.argv[1], 'r') outfile = open(sys.argv[2], 'w') -class ObjectParser: +class ObjectParser(object): def read(self, s, line): ''' Read an element of the FUNCTION_TABLE until the end (a comma or the end of FUNCTION_TABLE), returning that location diff --git a/tools/js_optimizer.py b/tools/js_optimizer.py index a96f487018add..718304235972f 100644 --- a/tools/js_optimizer.py +++ b/tools/js_optimizer.py @@ -201,7 +201,7 @@ def use_native(x, source_map=False): if type(x) == str: return x in NATIVE_PASSES return len(NATIVE_PASSES.intersection(x)) == len(x) and 'asm' in x -class Minifier: +class Minifier(object): ''' asm.js minification support. We calculate minification of globals here, then pass that into the parallel js-optimizer.js runners which @@ -347,7 +347,7 @@ def run_on_js(filename, passes, js_engine, source_map=False, extra_info=None, ju post = js[end_funcs + len(end_funcs_marker):] js = js[start_funcs + len(start_funcs_marker):end_funcs] if 'asm' not in passes: # can have Module[..] and inlining prevention code, push those to post - class Finals: + class Finals(object): buf = [] def process(line): if len(line) > 0 and (line.startswith(('Module[', 'if (globalScope)')) or line.endswith('["X"]=1;')): diff --git a/tools/shared.py b/tools/shared.py index f8786071f98f3..ec07da946c042 100644 --- a/tools/shared.py +++ b/tools/shared.py @@ -13,7 +13,7 @@ # On Windows python suffers from a particularly nasty bug if python is spawning new processes while python itself is spawned from some other non-console process. # Use a custom replacement for Popen on Windows to avoid the "WindowsError: [Error 6] The handle is invalid" errors when emcc is driven through cmake or mingw32-make. # See http://bugs.python.org/issue3905 -class WindowsPopen: +class WindowsPopen(object): def __init__(self, args, bufsize=0, executable=None, stdin=None, stdout=None, stderr=None, preexec_fn=None, close_fds=False, shell=False, cwd=None, env=None, universal_newlines=False, startupinfo=None, creationflags=0): self.stdin = stdin @@ -772,7 +772,7 @@ def clean_temp(): prepare_to_clean_temp(EMSCRIPTEN_TEMP_DIR) # this global var might change later return EMSCRIPTEN_TEMP_DIR -class WarningManager: +class WarningManager(object): warnings = { 'ABSOLUTE_PATHS': { 'enabled': False, # warning about absolute-paths is disabled by default @@ -822,7 +822,7 @@ def warn(warning_type, message=None): warning['printed'] = True logging.warning((message or warning['message']) + ' [-W' + warning_type.lower().replace('_', '-') + ']') -class Configuration: +class Configuration(object): def __init__(self, environ=os.environ): self.DEBUG = environ.get('EMCC_DEBUG') if self.DEBUG == "0": @@ -1130,7 +1130,7 @@ def expand_byte_size_suffixes(value): # Settings. A global singleton. Not pretty, but nicer than passing |, settings| everywhere class Settings2(type): - class __impl: + class __impl(object): attrs = {} def __init__(self): @@ -1283,7 +1283,7 @@ def extract_archive_contents(f): 'files': [] } -class ObjectFileInfo: +class ObjectFileInfo(object): def __init__(self, returncode, output, defs=set(), undefs=set(), commons=set()): self.returncode = returncode self.output = output @@ -1306,7 +1306,7 @@ def g_multiprocessing_initializer(*args): # Building -class Building: +class Building(object): COMPILER = CLANG LLVM_OPTS = False COMPILER_TEST_OPTS = [] # For use of the test runner @@ -1324,7 +1324,7 @@ def get_multiprocessing_pool(): # If running with one core only, create a mock instance of a pool that does not # actually spawn any new subprocesses. Very useful for internal debugging. if cores == 1: - class FakeMultiprocessor: + class FakeMultiprocessor(object): def map(self, func, tasks): results = [] for t in tasks: @@ -2299,7 +2299,7 @@ def reconfigure_cache(): global Cache Cache = cache.Cache(debug=DEBUG_CACHE) -class JS: +class JS(object): memory_initializer_pattern = '/\* memory initializer \*/ allocate\(\[([\d, ]*)\], "i8", ALLOC_NONE, ([\d+Runtime\.GLOBAL_BASEHgb]+)\);' no_memory_initializer_pattern = '/\* no memory initializer \*/' @@ -2466,7 +2466,7 @@ def split_initializer(contents): @staticmethod def replace_initializers(src, inits): - class State: + class State(object): first = True def rep(m): if not State.first: return '' @@ -2525,7 +2525,7 @@ def is_dyn_call(func): def is_function_table(name): return name.startswith('FUNCTION_TABLE_') -class WebAssembly: +class WebAssembly(object): @staticmethod def lebify(x): assert x >= 0, 'TODO: signed' diff --git a/tools/system_libs.py b/tools/system_libs.py index 053b097442895..90232d352660c 100644 --- a/tools/system_libs.py +++ b/tools/system_libs.py @@ -305,7 +305,7 @@ def add_back_deps(need): symbolses = shared.Building.parallel_llvm_nm(map(os.path.abspath, temp_files)) if len(symbolses) == 0: - class Dummy: + class Dummy(object): defs = set() undefs = set() symbolses.append(Dummy()) @@ -430,7 +430,7 @@ def do_create(): import ports -class Ports: +class Ports(object): @staticmethod def build_port(src_path, output_path, includes=[], flags=[], exclude_files=[], exclude_dirs=[]): srcs = [] @@ -483,7 +483,7 @@ def fetch_project(name, url, subdir): logging.debug(' (at ' + fullname + ')') Ports.name_cache.add(name) - class State: + class State(object): retrieved = False unpacked = False diff --git a/tools/tempfiles.py b/tools/tempfiles.py index 3e1b25a22d895..f3fdb48c04899 100644 --- a/tools/tempfiles.py +++ b/tools/tempfiles.py @@ -27,7 +27,7 @@ def remove_readonly_and_try_again(func, path, exc_info): except: pass -class TempFiles: +class TempFiles(object): def __init__(self, tmp, save_debug_files=False): self.tmp = tmp self.save_debug_files = save_debug_files @@ -48,7 +48,7 @@ def get_file(self, suffix): """Returns an object representing a RAII-like access to a temp file, that has convenient pythonesque semantics for being used via a construct 'with TempFiles.get_file(..) as filename:'. The file will be deleted immediately once the 'with' block is exited.""" - class TempFileObject: + class TempFileObject(object): def __enter__(self_): self_.file = tempfile.NamedTemporaryFile(dir=self.tmp, suffix=suffix, delete=False) self_.file.close() # NamedTemporaryFile passes out open file handles, but callers prefer filenames (and open their own handles manually if needed) diff --git a/tools/toolchain_profiler.py b/tools/toolchain_profiler.py index 2dbc00b474d09..00c89652b5397 100644 --- a/tools/toolchain_profiler.py +++ b/tools/toolchain_profiler.py @@ -50,7 +50,7 @@ def profiled_check_output(cmd, *args, **kw): ToolchainProfiler.record_subprocess_finish(pid, 0) return ret - class ProfiledPopen: + class ProfiledPopen(object): def __init__(self, args, bufsize=0, executable=None, stdin=None, stdout=None, stderr=None, preexec_fn=None, close_fds=False, shell=False, cwd=None, env=None, universal_newlines=False, startupinfo=None, creationflags=0): self.process = original_Popen(args, bufsize, executable, stdin, stdout, stderr, preexec_fn, close_fds, shell, cwd, env, universal_newlines, startupinfo, creationflags) @@ -79,7 +79,7 @@ def kill(self): subprocess.check_output = profiled_check_output subprocess.Popen = ProfiledPopen - class ToolchainProfiler: + class ToolchainProfiler(object): # Provide a running counter towards negative numbers for PIDs for which we don't know what the actual process ID is imaginary_pid_ = 0 profiler_logs_path = None # Log file not opened yet @@ -182,7 +182,7 @@ def exit_all_blocks(): for b in ToolchainProfiler.block_stack[::-1]: ToolchainProfiler.exit_block(b) - class ProfileBlock: + class ProfileBlock(object): def __init__(self, block_name): self.block_name = block_name @@ -204,7 +204,7 @@ def imaginary_pid(): else: exit = sys.exit - class ToolchainProfiler: + class ToolchainProfiler(object): @staticmethod def record_process_start(): pass @@ -233,7 +233,7 @@ def enter_block(block_name): def exit_block(block_name): pass - class ProfileBlock: + class ProfileBlock(object): def __init__(self, block_name): pass def __enter__(self): diff --git a/tools/webidl_binder.py b/tools/webidl_binder.py index 3633b76e44101..9e4334c494f51 100644 --- a/tools/webidl_binder.py +++ b/tools/webidl_binder.py @@ -25,7 +25,7 @@ if DEBUG: print "Debug print ON, CHECKS=%s" % CHECKS -class Dummy: +class Dummy(object): def __init__(self, init): for k, v in init.iteritems(): self.__dict__[k] = v From 04eae7bde0788a8caf2a971de4966b4c4c1e9832 Mon Sep 17 00:00:00 2001 From: Sam Clegg Date: Thu, 15 Jun 2017 14:31:19 -0700 Subject: [PATCH 16/16] Use "X not in Y" rather then "not X in Y" in python scripts (#5302) This is the more idomatic way to do it in python and less potentially abigous when combined with other terms. Also, add myself to AUTHORS file. --- AUTHORS | 1 + emcc.py | 4 ++-- tests/benchmark_sse1.py | 6 +++--- tests/test_sockets.py | 2 +- third_party/WebIDL.py | 2 +- third_party/websockify/websockify/websocketproxy.py | 2 +- tools/ffdb.py | 6 +++--- tools/shared.py | 10 +++++----- 8 files changed, 17 insertions(+), 16 deletions(-) diff --git a/AUTHORS b/AUTHORS index 56faf4e662ff7..9637e855a69df 100644 --- a/AUTHORS +++ b/AUTHORS @@ -296,3 +296,4 @@ a license to everyone to use it as detailed in LICENSE.) * Yair Levinson (copyright owned by Autodesk, Inc.) * Matjaž Drolc * James Swift (copyright owned by PSPDFKit GmbH) +* Sam Clegg (copyright owned by Google, Inc. diff --git a/emcc.py b/emcc.py index c83917104a1dd..6765aacc25194 100755 --- a/emcc.py +++ b/emcc.py @@ -825,7 +825,7 @@ def get_last_setting_change(setting): # If not compiling to JS, then we are compiling to an intermediate bitcode objects or library, so # ignore dynamic linking, since multiple dynamic linkings can interfere with each other - if not filename_type_suffix(target) in JS_CONTAINING_SUFFIXES or options.ignore_dynamic_linking: + if filename_type_suffix(target) not in JS_CONTAINING_SUFFIXES or options.ignore_dynamic_linking: def check(input_file): if filename_type_ending(input_file) in DYNAMICLIB_ENDINGS: if not options.ignore_dynamic_linking: logging.warning('ignoring dynamic library %s because not compiling to JS or HTML, remember to link it when compiling to JS or HTML at the end', os.path.basename(input_file)) @@ -2172,7 +2172,7 @@ def binaryen_method_sanity_check(): methods = shared.Settings.BINARYEN_METHOD.split(',') valid_methods = ['asmjs', 'native-wasm', 'interpret-s-expr', 'interpret-binary', 'interpret-asm2wasm'] for m in methods: - if not m.strip() in valid_methods: + if m.strip() not in valid_methods: logging.error('Unrecognized BINARYEN_METHOD "' + m.strip() + '" specified! Please pass a comma-delimited list containing one or more of: ' + ','.join(valid_methods)) sys.exit(1) diff --git a/tests/benchmark_sse1.py b/tests/benchmark_sse1.py index 9c9826ed5d98c..6a75807f38d41 100755 --- a/tests/benchmark_sse1.py +++ b/tests/benchmark_sse1.py @@ -120,11 +120,11 @@ def strip_comments(text): charts_html = {} for result in native_results['results']: ch = result['chart'] - if not ch in charts_native: charts_native[ch] = [] + if ch not in charts_native: charts_native[ch] = [] charts_native[ch] += [result] for result in html_results['results']: ch = result['chart'] - if not ch in charts_html: charts_html[ch] = [] + if ch not in charts_html: charts_html[ch] = [] charts_html[ch] += [result] def find_result_in_category(results, category): @@ -298,4 +298,4 @@ def format_comparison(a, b): html += '' open('results_sse1.html', 'w').write(html) -print 'Wrote ' + str(len(html)) + ' bytes to file results_sse1.html.' \ No newline at end of file +print 'Wrote ' + str(len(html)) + ' bytes to file results_sse1.html.' diff --git a/tests/test_sockets.py b/tests/test_sockets.py index 6d61423658c32..d57022c69c232 100644 --- a/tests/test_sockets.py +++ b/tests/test_sockets.py @@ -587,7 +587,7 @@ def test_webrtc(self): # XXX see src/settings.js, this is disabled pending inves def test_nodejs_sockets_echo(self): # This test checks that sockets work when the client code is run in Node.js # Run with ./runner.py sockets.test_nodejs_sockets_echo - if not NODE_JS in JS_ENGINES: + if NODE_JS not in JS_ENGINES: return self.skip('node is not present') sockets_include = '-I'+path_from_root('tests', 'sockets') diff --git a/third_party/WebIDL.py b/third_party/WebIDL.py index ec0076786df67..7f3263f37e52c 100644 --- a/third_party/WebIDL.py +++ b/third_party/WebIDL.py @@ -962,7 +962,7 @@ def addExtendedAttributes(self, attrs): newMethod = self.parentScope.lookupIdentifier(method.identifier) if newMethod == method: self.namedConstructors.append(method) - elif not newMethod in self.namedConstructors: + elif newMethod not in self.namedConstructors: raise WebIDLError("NamedConstructor conflicts with a NamedConstructor of a different interface", [method.location, newMethod.location]) elif (identifier == "ArrayClass"): diff --git a/third_party/websockify/websockify/websocketproxy.py b/third_party/websockify/websockify/websocketproxy.py index c098023e60f92..becf0d642a471 100755 --- a/third_party/websockify/websockify/websocketproxy.py +++ b/third_party/websockify/websockify/websocketproxy.py @@ -110,7 +110,7 @@ def get_target(self, target_plugin, path): # Extract the token parameter from url args = parse_qs(urlparse(path)[4]) # 4 is the query from url - if not 'token' in args or not len(args['token']): + if 'token' not in args or not len(args['token']): raise self.server.EClose("Token not present") token = args['token'][0].rstrip('\n') diff --git a/tools/ffdb.py b/tools/ffdb.py index 93fae51859f89..f40b868e4f7b8 100755 --- a/tools/ffdb.py +++ b/tools/ffdb.py @@ -292,7 +292,7 @@ def delete_temp_file(): cur_time = time.time() secs_elapsed = cur_time - start_time print 'Upload of ' + sizeof_fmt(file_size) + ' finished. Total time elapsed: ' + str(int(secs_elapsed)) + ' seconds. Data rate: {:5.2f} KB/second.'.format(file_size / 1024.0 / secs_elapsed) - if not 'appId' in reply: + if 'appId' not in reply: print 'Error: Application install failed! ' + str(reply) sys.exit() return reply['appId'] @@ -591,7 +591,7 @@ def main(): logv('Connected. Handshake: ' + str(handshake)) data = send_b2g_cmd('root', 'listTabs') - if not 'deviceActor' in data: + if 'deviceActor' not in data: print 'Error! Debugging connection was not available. Make sure that the "Remote debugging" developer option on the device is set to "ADB and Devtools".' sys.exit(1) deviceActorName = data['deviceActor'] @@ -618,7 +618,7 @@ def main(): print ' No applications running.' else: print ' No applications installed.' - if not '--all' in sys.argv and not print_only_running: + if '--all' not in sys.argv and not print_only_running: print 'Not showing built-in apps that cannot be uninstalled. Pass --all to include those in the listing.' elif sys.argv[1] == 'launch' or sys.argv[1] == 'close' or sys.argv[1] == 'uninstall' or sys.argv[1] == 'getAppActor': if len(sys.argv) < 3: diff --git a/tools/shared.py b/tools/shared.py index ec07da946c042..589b9b36cbf71 100644 --- a/tools/shared.py +++ b/tools/shared.py @@ -203,7 +203,7 @@ def new(*args): if EM_CONFIG and not os.path.isfile(EM_CONFIG): if EM_CONFIG.startswith('-'): raise Exception('Passed --em-config without an argument. Usage: --em-config /path/to/.emscripten or --em-config EMSCRIPTEN_ROOT=/path/;LLVM_ROOT=/path;...') - if not '=' in EM_CONFIG: + if '=' not in EM_CONFIG: raise Exception('File ' + EM_CONFIG + ' passed to --em-config does not exist!') else: EM_CONFIG = EM_CONFIG.replace(';', '\n') + '\n' @@ -684,14 +684,14 @@ def get_clang_native_env(): if not os.path.isdir(windows10_sdk_dir): raise Exception('Windows 10 SDK was not found in "' + windows10_sdk_dir + '"! Run in Visual Studio command prompt to avoid the need to autoguess this location.') - if not 'VSINSTALLDIR' in env: env['VSINSTALLDIR'] = visual_studio_path - if not 'VCINSTALLDIR' in env: env['VCINSTALLDIR'] = os.path.join(visual_studio_path, 'VC') + env.setdefault('VSINSTALLDIR', visual_studio_path) + env.setdefault('VCINSTALLDIR', os.path.join(visual_studio_path, 'VC')) windows10sdk_kits_include_dir = os.path.join(windows10_sdk_dir, 'Include') windows10sdk_kit_version_name = [x for x in os.listdir(windows10sdk_kits_include_dir) if os.path.isdir(os.path.join(windows10sdk_kits_include_dir, x))][0] # e.g. "10.0.10150.0" or "10.0.10240.0" def append_item(key, item): - if not key in env or len(env[key].strip()) == 0: env[key] = item + if key not in env or len(env[key].strip()) == 0: env[key] = item else: env[key] = env[key] + ';' + item append_item('INCLUDE', os.path.join(env['VCINSTALLDIR'], 'INCLUDE')) @@ -1196,7 +1196,7 @@ def __getattr__(self, attr): raise AttributeError def __setattr__(self, attr, value): - if not attr in self.attrs: + if attr not in self.attrs: import difflib logging.warning('''Assigning a non-existent settings attribute "%s"''' % attr) suggestions = ', '.join(difflib.get_close_matches(attr, self.attrs.keys()))