emscripten-core / emscripten

Emscripten: An LLVM-to-WebAssembly Compiler
Other
25.74k stars 3.3k forks source link

Setting `--closure 1` causes `JSC_UNDEFINED_VARIABLE` errors #21732

Open oboukli opened 6 months ago

oboukli commented 6 months ago

Version of emscripten/emsdk:

docker run -it --rm emscripten/emsdk:3.1.56 emcc --version
emcc (Emscripten gcc/clang-like replacement + linker emulating GNU ld) 3.1.56 (cf90417346b78455089e64eb909d71d091ecc055)

Failing command line in full:

docker run -it --rm \
-v $(pwd):/src \
-v $(pwd)/emscripten_cache:/emsdk/upstream/emscripten/cache \
-v $(pwd)/emscripten_tmp:/tmp \
--workdir=/src emscripten/emsdk:3.1.56 \
emcc \
--closure 1 \
--use-port=sdl2 \
-l EGL \
-l GL \
-l html5 \
-s ASSERTIONS=1 \
-s ASYNCIFY \
-s ERROR_ON_UNDEFINED_SYMBOLS=1 \
-s EXPORTED_FUNCTIONS=_main \
-s FILESYSTEM=0 \
-s NO_EXIT_RUNTIME \
-s STRICT \
-s USE_CLOSURE_COMPILER \
-v \
-o index.html main.c

main.c

// Minimal SDL2 example

#include <emscripten.h>
#include <SDL2/SDL.h>

EMSCRIPTEN_KEEPALIVE int main() {
  SDL_Window* window = SDL_CreateWindow(
    "", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 128, 128, SDL_WINDOW_SHOWN);
  SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
  SDL_SetRenderDrawColor(renderer, 255, 127, 0, 255);
  SDL_RenderClear(renderer);
  SDL_RenderPresent(renderer);
}

Full link command and output with -v appended:

 /emsdk/upstream/bin/clang -target wasm32-unknown-emscripten -fignore-exceptions -fvisibility=default -mllvm -combiner-global-alias-analysis=false -mllvm -enable-emscripten-sjlj -mllvm -disable-lsr --sysroot=/emsdk/upstream/emscripten/cache/sysroot -Werror=implicit-function-declaration -isystem /emsdk/upstream/emscripten/cache/sysroot/include/SDL2 -Xclang -iwithsysroot/include/fakesdl -Xclang -iwithsysroot/include/compat -v main.c -c -o /tmp/emscripten_temp_f3tri3oj/main_0.o
clang version 19.0.0git (https:/github.com/llvm/llvm-project 34ba90745fa55777436a2429a51a3799c83c6d4c)
Target: wasm32-unknown-emscripten
Thread model: posix
InstalledDir: /emsdk/upstream/bin
 (in-process)
 "/emsdk/upstream/bin/clang-19" -cc1 -triple wasm32-unknown-emscripten -emit-obj -mrelax-all -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name main.c -mrelocation-model static -mframe-pointer=none -ffp-contract=on -fno-rounding-math -mconstructor-aliases -target-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/src -v -fcoverage-compilation-dir=/src -resource-dir /emsdk/upstream/lib/clang/19 -isystem /emsdk/upstream/emscripten/cache/sysroot/include/SDL2 -isysroot /emsdk/upstream/emscripten/cache/sysroot -internal-isystem /emsdk/upstream/lib/clang/19/include -internal-isystem /emsdk/upstream/emscripten/cache/sysroot/include/wasm32-emscripten -internal-isystem /emsdk/upstream/emscripten/cache/sysroot/include -Werror=implicit-function-declaration -ferror-limit 19 -fvisibility=default -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -fignore-exceptions -fcolor-diagnostics -iwithsysroot/include/fakesdl -iwithsysroot/include/compat -mllvm -combiner-global-alias-analysis=false -mllvm -enable-emscripten-sjlj -mllvm -disable-lsr -o /tmp/emscripten_temp_f3tri3oj/main_0.o -x c main.c
clang -cc1 version 19.0.0git based upon LLVM 19.0.0git default target x86_64-unknown-linux-gnu
ignoring nonexistent directory "/emsdk/upstream/emscripten/cache/sysroot/include/wasm32-emscripten"
#include "..." search starts here:
#include <...> search starts here:
 /emsdk/upstream/emscripten/cache/sysroot/include/SDL2
 /emsdk/upstream/emscripten/cache/sysroot/include/fakesdl
 /emsdk/upstream/emscripten/cache/sysroot/include/compat
 /emsdk/upstream/lib/clang/19/include
 /emsdk/upstream/emscripten/cache/sysroot/include
End of search list.
 /emsdk/upstream/bin/clang --version
 /emsdk/upstream/bin/wasm-ld -o index.wasm -lhtml5 /tmp/emscripten_temp_f3tri3oj/main_0.o -L/emsdk/upstream/emscripten/cache/sysroot/lib/wasm32-emscripten /emsdk/upstream/emscripten/cache/sysroot/lib/wasm32-emscripten/libSDL2.a --whole-archive -lGL-getprocaddr --no-whole-archive -lnoexit -lc-debug -ldlmalloc -lcompiler_rt -lsockets --fatal-warnings -mllvm -combiner-global-alias-analysis=false -mllvm -enable-emscripten-sjlj -mllvm -disable-lsr /tmp/tmp06pqoks6libemscripten_js_symbols.so --export=emscripten_stack_get_end --export=emscripten_stack_get_free --export=emscripten_stack_get_base --export=emscripten_stack_get_current --export=emscripten_stack_init --export=stackAlloc --export=stackSave --export=stackRestore --export=__get_temp_ret --export=__set_temp_ret --export=__wasm_call_ctors --export=malloc --export=free --export-if-defined=__start_em_asm --export-if-defined=__stop_em_asm --export-if-defined=__start_em_lib_deps --export-if-defined=__stop_em_lib_deps --export-if-defined=__start_em_js --export-if-defined=__stop_em_js --export-if-defined=main --export-if-defined=__main_argc_argv --export-if-defined=fflush --export-table -z stack-size=65536 --no-growable-memory --initial-heap=16777216 --no-entry --stack-first --table-base=1
 /emsdk/upstream/bin/llvm-objcopy index.wasm index.wasm --remove-section=.debug* --remove-section=producers
 /emsdk/upstream/bin/wasm-emscripten-finalize --pass-arg=legalize-js-interface-exported-helpers index.wasm -o index.wasm --detect-features
 /emsdk/node/16.20.0_64bit/bin/node /emsdk/upstream/emscripten/src/compiler.mjs /tmp/tmp4e9mvlxh.json
 /emsdk/upstream/bin/wasm-opt --asyncify --pass-arg=asyncify-asserts --pass-arg=asyncify-imports@env.invoke_*,env.__asyncjs__*,*.fd_sync,*.emscripten_promise_await,*.emscripten_sleep,*.emscripten_wget_data,*.emscripten_scan_registers,*.emscripten_lazy_load_code,*._load_secondary_module,*.emscripten_fiber_swap index.wasm -o index.wasm --mvp-features --enable-mutable-globals --enable-sign-ext
 /emsdk/node/16.20.0_64bit/bin/node --max_old_space_size=8192 /emsdk/upstream/emscripten/node_modules/.bin/google-closure-compiler --compilation_level ADVANCED_OPTIMIZATIONS --language_in ECMASCRIPT_2021 --language_out NO_TRANSPILE --emit_use_strict=false --externs /emsdk/upstream/emscripten/src/closure-externs/closure-externs.js --externs /emsdk/upstream/emscripten/src/closure-externs/node-externs.js --externs /emsdk/upstream/emscripten/third_party/closure-compiler/node-externs/buffer.js --externs /emsdk/upstream/emscripten/third_party/closure-compiler/node-externs/string_decoder.js --externs /emsdk/upstream/emscripten/third_party/closure-compiler/node-externs/os.js --externs /emsdk/upstream/emscripten/third_party/closure-compiler/node-externs/tty.js --externs /emsdk/upstream/emscripten/third_party/closure-compiler/node-externs/domain.js --externs /emsdk/upstream/emscripten/third_party/closure-compiler/node-externs/process.js --externs /emsdk/upstream/emscripten/third_party/closure-compiler/node-externs/readline.js --externs /emsdk/upstream/emscripten/third_party/closure-compiler/node-externs/path.js --externs /emsdk/upstream/emscripten/third_party/closure-compiler/node-externs/vm.js --externs /emsdk/upstream/emscripten/third_party/closure-compiler/node-externs/dgram.js --externs /emsdk/upstream/emscripten/third_party/closure-compiler/node-externs/events.js --externs /emsdk/upstream/emscripten/third_party/closure-compiler/node-externs/util.js --externs /emsdk/upstream/emscripten/third_party/closure-compiler/node-externs/dns.js --externs /emsdk/upstream/emscripten/third_party/closure-compiler/node-externs/zlib.js --externs /emsdk/upstream/emscripten/third_party/closure-compiler/node-externs/punycode.js --externs /emsdk/upstream/emscripten/third_party/closure-compiler/node-externs/url.js --externs /emsdk/upstream/emscripten/third_party/closure-compiler/node-externs/cluster.js --externs /emsdk/upstream/emscripten/third_party/closure-compiler/node-externs/stream.js --externs /emsdk/upstream/emscripten/third_party/closure-compiler/node-externs/repl.js --externs /emsdk/upstream/emscripten/third_party/closure-compiler/node-externs/child_process.js --externs /emsdk/upstream/emscripten/third_party/closure-compiler/node-externs/https.js --externs /emsdk/upstream/emscripten/third_party/closure-compiler/node-externs/querystring.js --externs /emsdk/upstream/emscripten/third_party/closure-compiler/node-externs/net.js --externs /emsdk/upstream/emscripten/third_party/closure-compiler/node-externs/tls.js --externs /emsdk/upstream/emscripten/third_party/closure-compiler/node-externs/fs.js --externs /emsdk/upstream/emscripten/third_party/closure-compiler/node-externs/http.js --externs /emsdk/upstream/emscripten/third_party/closure-compiler/node-externs/core.js --externs /emsdk/upstream/emscripten/src/closure-externs/dyncall-externs.js --js /tmp/emscripten_temp_f3tri3oj/index.js --js_output_file tmp2b31moes.cc.js --formatting PRETTY_PRINT
building:ERROR: Closure compiler run failed:

building:ERROR: /tmp/emscripten_temp_f3tri3oj/index.js:1042:183: ERROR - [JSC_UNDEFINED_VARIABLE] variable allocate is undeclared
  1042|  132338: ($0) => { var str = UTF8ToString($0) + '\n\n' + 'Abort/Retry/Ignore/AlwaysIgnore? [ariA] :'; var reply = window.prompt(str, "i"); if (reply === null) { reply = "i"; } return allocate(intArrayFromString(reply), 'i8', ALLOC_NORMAL); },  
                                                                                                                                                                                               ^^^^^^^^

/tmp/emscripten_temp_f3tri3oj/index.js:1042:192: ERROR - [JSC_UNDEFINED_VARIABLE] variable intArrayFromString is undeclared
  1042|  132338: ($0) => { var str = UTF8ToString($0) + '\n\n' + 'Abort/Retry/Ignore/AlwaysIgnore? [ariA] :'; var reply = window.prompt(str, "i"); if (reply === null) { reply = "i"; } return allocate(intArrayFromString(reply), 'i8', ALLOC_NORMAL); },  
                                                                                                                                                                                                        ^^^^^^^^^^^^^^^^^^

/tmp/emscripten_temp_f3tri3oj/index.js:1042:225: ERROR - [JSC_UNDEFINED_VARIABLE] variable ALLOC_NORMAL is undeclared
  1042|  132338: ($0) => { var str = UTF8ToString($0) + '\n\n' + 'Abort/Retry/Ignore/AlwaysIgnore? [ariA] :'; var reply = window.prompt(str, "i"); if (reply === null) { reply = "i"; } return allocate(intArrayFromString(reply), 'i8', ALLOC_NORMAL); },  
                                                                                                                                                                                                                                         ^^^^^^^^^^^^

3 error(s), 0 warning(s)

emcc: error: closure compiler failed (rc: 3): /emsdk/node/16.20.0_64bit/bin/node --max_old_space_size=8192 /emsdk/upstream/emscripten/node_modules/.bin/google-closure-compiler --compilation_level ADVANCED_OPTIMIZATIONS --language_in ECMASCRIPT_2021 --language_out NO_TRANSPILE --emit_use_strict=false --externs /emsdk/upstream/emscripten/src/closure-externs/closure-externs.js --externs /emsdk/upstream/emscripten/src/closure-externs/node-externs.js --externs /emsdk/upstream/emscripten/third_party/closure-compiler/node-externs/buffer.js --externs /emsdk/upstream/emscripten/third_party/closure-compiler/node-externs/string_decoder.js --externs /emsdk/upstream/emscripten/third_party/closure-compiler/node-externs/os.js --externs /emsdk/upstream/emscripten/third_party/closure-compiler/node-externs/tty.js --externs /emsdk/upstream/emscripten/third_party/closure-compiler/node-externs/domain.js --externs /emsdk/upstream/emscripten/third_party/closure-compiler/node-externs/process.js --externs /emsdk/upstream/emscripten/third_party/closure-compiler/node-externs/readline.js --externs /emsdk/upstream/emscripten/third_party/closure-compiler/node-externs/path.js --externs /emsdk/upstream/emscripten/third_party/closure-compiler/node-externs/vm.js --externs /emsdk/upstream/emscripten/third_party/closure-compiler/node-externs/dgram.js --externs /emsdk/upstream/emscripten/third_party/closure-compiler/node-externs/events.js --externs /emsdk/upstream/emscripten/third_party/closure-compiler/node-externs/util.js --externs /emsdk/upstream/emscripten/third_party/closure-compiler/node-externs/dns.js --externs /emsdk/upstream/emscripten/third_party/closure-compiler/node-externs/zlib.js --externs /emsdk/upstream/emscripten/third_party/closure-compiler/node-externs/punycode.js --externs /emsdk/upstream/emscripten/third_party/closure-compiler/node-externs/url.js --externs /emsdk/upstream/emscripten/third_party/closure-compiler/node-externs/cluster.js --externs /emsdk/upstream/emscripten/third_party/closure-compiler/node-externs/stream.js --externs /emsdk/upstream/emscripten/third_party/closure-compiler/node-externs/repl.js --externs /emsdk/upstream/emscripten/third_party/closure-compiler/node-externs/child_process.js --externs /emsdk/upstream/emscripten/third_party/closure-compiler/node-externs/https.js --externs /emsdk/upstream/emscripten/third_party/closure-compiler/node-externs/querystring.js --externs /emsdk/upstream/emscripten/third_party/closure-compiler/node-externs/net.js --externs /emsdk/upstream/emscripten/third_party/closure-compiler/node-externs/tls.js --externs /emsdk/upstream/emscripten/third_party/closure-compiler/node-externs/fs.js --externs /emsdk/upstream/emscripten/third_party/closure-compiler/node-externs/http.js --externs /emsdk/upstream/emscripten/third_party/closure-compiler/node-externs/core.js --externs /emsdk/upstream/emscripten/src/closure-externs/dyncall-externs.js --js /tmp/emscripten_temp_f3tri3oj/index.js --js_output_file tmp2b31moes.cc.js --formatting PRETTY_PRINT

Note that the above builds correctly on emsdk v3.1.47 (using -sUSE_SDL=2 instead of --use-port=sdl2)

sbc100 commented 6 months ago

Hmm.. it looks like SDL is using some JS library symbols that are not present by default.

We should fix that upstream in SDL.

In the mean time you can do -sDEFAULT_LIBRARY_FUNCS_TO_INCLUDE=allocate,ALLOC_NORMAL,intArrayFromString to include those symbols in your build.

oboukli commented 6 months ago

Tried that and got new errors. BTW, changing FILESYSTEM also changes the error.

Note that in the minimal example above, it seems that, at least since v3.1.56, it's required to add -lEGL -lGL -lhtml5 although I'm not sure the docs mention -lhtml5 explicitly.

I've been unable to build with a similar setup since after v3.1.47 . Perhaps tests with strict and fine-tuned settings could be missing.

sbc100 commented 6 months ago

The requirement to explicitly add -lEGL -lGL -lhtml5 is because you are using -sSTRICT, and this sets -sAUTO_JS_LIBRARIES=0 which means you have to explicitly add those libraries, they don't get included automatically.

See https://emscripten.org/docs/tools_reference/settings_reference.html?highlight=environment#strict