Dynamic linking error with SDL (cannot resolve symbol SDL_SetHint
, but SDL_Init
works)
#4707
-
Hello, I'm trying to build a library (imgui-bundle) with pyodide, and I am building it in tree with the docker container. I can successfully build a package. However, when I'm trying to run it, I get this error message:
This error message comes from one pyodide patch to Emscripten. However I am quite convinced that my program is linked to SDL, since in the following function, a call to void RunnerSdl2::Impl_InitPlatformBackend()
{
auto flags = SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_GAMECONTROLLER;
#ifdef __EMSCRIPTEN__
flags = SDL_INIT_VIDEO | SDL_INIT_TIMER;
#endif
// The call to SDL_Init succeeds...
printf("About to call SDL_init\n");
if (SDL_Init(flags) != 0)
{
HIMG_ERROR(
std::string("RunnerSdlOpenGl3::Impl_InitPlatformBackend error ")
+ SDL_GetError());
}
printf("After SDL_init\n");
// But the call to SDL_SetHint will raise the dynamic linking error
SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1");
printf("After SDL_SetHint\n");
... I examined the linking command, here are the interesting bits below. SDL2 is linked via
In case it is helpful, here is the full call javascript callstack:
I'm posting this as a question since I don't know if it is an issue on my side (which is likely), or in pyodide. Any help would be appreciated. Thanks! |
Beta Was this translation helpful? Give feedback.
Replies: 5 comments
-
Could you please share the full build command and error log to reproduce the error? Also, it would be helpful if you could include the When it comes to linking SDL libraries, there are some behaviors that are handled internally by emscripten, and I've seen some issues where it doesn't work correctly in some cases, so I'd have to dig a little deeper to find out why. |
Beta Was this translation helpful? Give feedback.
-
Many thanks for you fast answer and your attention. I'll try to create a reproducible build environment, and keep you posted |
Beta Was this translation helpful? Give feedback.
-
I managed to create a minimum reproducible example. The dynamic linking error still happens with this minimal example, so I begin to think there is an actual issue here. As you will see it uses CMake / pybind11 / scikit-build-core to build a C++ library with bindings to Python. Below are some more details about this example: Folder structure
pyproject.toml [build-system]
requires = ["scikit-build-core>=0.3.3", "pybind11"]
build-backend = "scikit_build_core.build"
[project]
name = "daft_lib"
version = "0.0.1"
description="daft-lib"
requires-python = ">=3.7"
[tool.scikit-build]
wheel.expand-macos-universal-tags = true
wheel.packages = ["src/python_bindings/daft_lib"]
cmake.verbose = true
logging.level = "INFO" meta.yaml package:
name: daft_lib
version: 0.0.1
top-level:
- daft_lib
source:
path: /workspaces/pyodide/packages/daft_lib/daft_lib_src CMakeLists.txt cmake_minimum_required(VERSION 3.15...3.27)
project(litgen_template VERSION "0.0.1")
set(CMAKE_CXX_STANDARD 20)
# Build C++ library
find_package(SDL2 REQUIRED)
add_library(DaftLib STATIC src/DaftLib/DaftLib.cpp src/DaftLib/DaftLib.h)
target_link_libraries(DaftLib PUBLIC SDL2::SDL2 SDL2::SDL2main)
target_include_directories(DaftLib PUBLIC src/DaftLib)
if (EMSCRIPTEN)
# Interestingly, we do not have to add those options
# Since emscripten and/or pyodide seem to already add those
# (Is this because a dependency to SDL2 was found? I don't know)
# target_compile_options(DaftLib PUBLIC -sUSE_SDL=2)
# target_link_options(DaftLib PUBLIC -sUSE_SDL=2)
endif()
# Build bindings
include(litgen_cmake/litgen_cmake.cmake)
litgen_find_pybind11()
pybind11_add_module(_daft_lib src/python_bindings/module.cpp)
litgen_setup_module(
DaftLib # The C++ library for which we are building bindings
_daft_lib # The native python module name
daft_lib # This is the python wrapper around the native module
${CMAKE_CURRENT_LIST_DIR}/src/python_bindings
) DaftLib.cpp #include <SDL.h>
#include "DaftLib.h"
int DummyHandleAppEvents(void *, SDL_Event *) { return 0; }
// This is a dummy function that calls SDL functions.
// It is used to test the linking of the SDL library.
void dummy_sdl_call()
{
auto flags = SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_GAMECONTROLLER;
#ifdef __EMSCRIPTEN__
flags = SDL_INIT_VIDEO | SDL_INIT_TIMER;
#endif
// The call to SDL_Init succeeds...
printf("About to call SDL_init\n");
if (SDL_Init(flags) != 0)
fprintf(stderr, "Error: %s\n", SDL_GetError());
printf("After SDL_init\n");
// But the call to SDL_SetHint raise "Dynamic linking error: cannot resolve symbol"
printf("About to call SDL_SetHint\n");
SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1");
printf("After SDL_SetHint\n");
// SDL_SetEventFilter also raises this error
printf("About to call SDL_SetEventFilter\n");
SDL_SetEventFilter(DummyHandleAppEvents, nullptr);
printf("After SDL_SetEventFilter\n");
// The call to SQL_Quit succeeds
printf("About to call SDL_Quit\n");
SDL_Quit();
printf("After SDL_Quit\n");
} _daft_lib_test.html <!doctype html>
<html>
<head>
<script src="pyodide.js"></script>
</head>
<body>
<canvas id="canvas"></canvas>
<script type="text/javascript">
async function main(){
let pyodide = await loadPyodide();
pyodide._api._skip_unwind_fatal_error = true;
await pyodide.loadPackage(["daft_lib"], { checkIntegrity: false })
canvas = document.getElementById("canvas");
pyodide.canvas.setCanvas2D(canvas);
python_code = `
import daft_lib
async def main():
daft_lib.dummy_sdl_call()
main()
`
pyodide.runPythonAsync(python_code);
}
main();
</script>
</body>
</html> Full build log export EMCC_DEBUG=1
PYODIDE_PACKAGES="daft_lib" make (I'm not sure EMCC_DEBUG worked, since the build uses an isolated environment)
|
Beta Was this translation helpful? Give feedback.
-
It seems like SDL2 library is not linked when creating the final Anyway, here is the way you can fix it, inside build:
cflags: |
-sUSE_SDL=2
ldflags: |
-sUSE_SDL=2
-lSDL2 This will inject On a side note, I noticed that a binary for macOS ( |
Beta Was this translation helpful? Give feedback.
-
Thank you so much! This solves my issue. What is strange is that I looked carefully at the new logs and I did not see any difference after adding those flags. So I guess those flags are used in the install step, but without log... |
Beta Was this translation helpful? Give feedback.
It seems like SDL2 library is not linked when creating the final
.so
file. So it tries to find the symbol from outside at the runtime.I don't know the exact reason. I suspect CMake or Emscripten is not putting the flag for some reason.
Anyway, here is the way you can fix it, inside
meta.yaml
file, put the following content.This will inject
-lSDL2
at the final link step, which will force link SDL2 library to the final shared library.On a side note, I noticed that a binary for macOS (
_daft_lib.cpython-312-darwin.so
) exists in the built wheel. I would recommend to remove this from the wheel to prevent unexpected b…