Browser build in any plans?

I had tried --use-python but gives this error:

Host Python version (python3.8) must be the same as target Python version (python2.7)!
Build terminated.

even with --python-incdir

I have been trying to find where it defines the target as 2.7, haven’t found it yet.

What did you pass as --python-libdir? makepanda looks for a libpythonN.N.so (or .a) file, in the given library directory. It sounds like you have a libpython2.7.so or libpython2.7.a in that directory.

The code in question (that assigns SDK["PYTHONVERSION"]) is in SdkLocatePython in makepanda/makepandacore.py, line 2132.

Maybe you still have the thirdparty archive with Python 2.7 I provided at some point extracted to your source directory? If so, delete thirdparty/emscripten-libs/python, since it is taking precedence.

My bad. I thought it would ignore thirdparty if I put the --python-libdir. I replaced all the includes with python3.8 in thirdparty but it still had a ibpython2.7.a.

Build

python makepanda/makepanda.py --use-png --use-egg --use-zlib --no-freetype --use-bullet --no-openssl --use-direct --use-pview --use-gles2 --use-openal --use-python --use-vorbis --optimize 4 --outputdir embuilt --target emscripten --no-egl --use-freetype

terminated with an error I should be able to track down but have not yet:

Error: Aborting compilation due to previous errors
em++: error: ‘/Users/davidmarques/prog/emsdk/node/12.18.1_64bit/bin/node /Users/davidmarques/prog/emsdk/upstream/emscripten/src/compiler.js /var/folders/n7/djb71lls55v7nqfm8fdv6rg40000gn/T/tmp989b439j.txt’ failed (1)
Storing dependency cache.
Elapsed Time: 42 min 30 sec
The following command returned a non-zero value: em++ -o embuilt/bin/deploy-stub.js -Lembuilt/lib -Lembuilt/tmp embuilt/tmp/cpython-38-darwin/deploy-stub.bc -s WARN_ON_UNDEFINED_SYMBOLS=1 --memory-init-file 0 -s EXIT_RUNTIME=1 thirdparty/emscripten-libs/python/lib/libpython3.8.a -O3

But, the build did create core.so, direct.so, and physics.so so I included them in my app build, and it seemed ok (about a hundred undefined _ZN…) – but still nothing when it runs, and no error. I am looking now to see if the problem is static vs shared libraries.

full app build trace

Not entirely true. There was an error (below) but from my reading of the source code continues with the fallback – “falling back to ArrayBuffer instantiation”. So I am not sure what to try next, I don’t know what is not working.

wasm streaming compile failed: TypeError: Response has unsupported MIME type editor.html:1:516
printErr http://ideategames.org/museum/editor.html:1
instantiateAsync http://ideategames.org/museum/editor.js:1
(Async: promise callback)
instantiateAsync http://ideategames.org/museum/editor.js:1
(Async: promise callback)
instantiateAsync http://ideategames.org/museum/editor.js:1
createWasm http://ideategames.org/museum/editor.js:1
http://ideategames.org/museum/editor.js:1

Sideways progress (?). core and direct are not getting linked in. This shows up in the build (link errors below) and then running the program errors on “missing function: initcore”.

I assume this has to do with them being shared instead of static libraries? Unless the init function name has changed? (init_core and Pyinit_core do not work either, and the same result if I build one of your sample programs).

These errors in the build seem to indicate the problem:

WARNING: Python cannot import extension modules under frozen Python packages; panda3d.core will be inaccessible. passing either -l to link in extension modules or use -x panda3d to exclude the entire package.
WARNING: Python cannot import extension modules under frozen Python packages; panda3d.direct will be inaccessible. passing either -l to link in extension modules or use -x panda3d to exclude the entire package.
WARNING: Python cannot import extension modules under frozen Python packages; panda3d.physics will be inaccessible. passing either -l to link in extension modules or use -x panda3d to exclude the entire package.

I haven’t figured out where to add the -l, I get an error when I put it on the emcc line. Still working on the idea that they are not compiled in properly, but not sure how to get there.

It’s PyInit_core, with capital I.

The -l flag is to pfreeze, which sets freezer.linkExtensionModules = True, so you could do that, but you could also just ignore the warning and make sure you link in core.bc and direct.bc yourself and call PyInit_core, etc.

Oops, sorry, I meant to do that variant also, forgot.

Gets rid of the errors, but just a blank app (here is the link again)

I have all the Python init commented out except PyInit_time and PyInit_hashlib. The others give errors, but I am going through one by one to be sure.

BTW, PyInit_core and PyInit_direct and PyInit_time wanted to be defined as extern int instead of extern void.

And, just to be clear, I don’t have core.bc but core.so (and with direct). But it seems to work, the init is called.

The number of undefined _ZN… is about half what it was.

I turned on python verbose and updated the build trace.

Building Asteroid has the same result.

This is great progress! In the JS console, I can see that the Python interpreter is being loaded successfully and that various modules are being imported.

It’s just that no code is being executed now; I would need to see your C code generated by your freeze script to see why.

Note that to correctly serve WebAssembly, you need to configure your web server to recognise wasm files as having the application/wasm MIME type.

Great. If you can get it working, in return I will replicate the process twice on new systems and write up the process in detail.

I trapped the editor.c and put it on my server here

I also put the entire directory a Maze.zip in case you want to look into what I did.

I am a little confused about the application/wasm. I looked at the js code and it seems the code already has a workaround (resulting in “falling back to ArrayBuffer instantiation”) and that code looks functionally the same as recommended in stack overflow.

It’s a web server configuration thing; it looks like you’re using Apache, so you need to set AddType application/wasm .wasm somewhere in the Apache configuration. But, as you said, it does have a fall back, but perhaps it is less efficient this way.

The C code in the freezify.py that I gave you is what I used for my live editor program; it defines a runPythonCode function that is called from some other JavaScript I used on that page.

In fact, if you type this into the JS console, you’ll see that it does execute Python code:

runFunc = Module.cwrap('runPythonCode', 'number', ['string']);
runFunc("print('Hello, world!')");

(The __builtin__ errors also come from the outdated code I put in freezify.py; you can just take that out)

What you probably want is take out that stuff from the C code (which is defined in freezify.py) and replace it with something that simply imports your main module, like so:

if (PyImport_ImportFrozenModule("__main__") < 0) {
  PyErr_Print();
}

ModuleNotFoundError: No module named ‘panda3d.core’; ‘panda3d’ is not a package

Interesting naming. I linked ‘core’ but not ‘panda3d.core’. Clearly panda3d is not being loaded correctly.

BTW, I inserted the line here, just to be sure it is what you meant:

EMSCRIPTEN_KEEPALIVE void loadPanda() {
PyObject *panda3d_module = PyImport_AddModule("panda3d");
PyInit_core();

PyObject *panda3d_dict = PyModule_GetDict(panda3d_module);
PyDict_SetItemString(panda3d_dict, "core", PyImport_ImportModule("panda3d.core"));

PyInit_direct();
init_libOpenALAudio();

EM_ASM({
    Module.setStatus('Done loading Museum.');
});
if (PyImport_ImportFrozenModule("__main__") < 0) {
  PyErr_Print();
}

}

I think it should be after “Starting Python…”, so trying it there. (doesn’t work inside Py_FrozenMain as you probably knew)

I think that should be about right.

There might have been changes to Python’s import system that prevent this import hack from working. We might have to do it a different way. Let me see what I can find out.

I think you might need at the very least this, at the beginning of loadPanda():

  PyObject *panda3d_module = PyImport_AddModule("panda3d");
  PyModule_AddStringConstant(panda3d_module, "__package__", "panda3d");
  PyModule_AddObject(panda3d_module, "__path__", PyList_New(0));

This should tell Python that, yes, really, “panda3d” is a package.

Sorry, I wasn’t clear. I know panda3d is a package, I just meant that loading core module by itself not in the context of panda3d package puzzled me, but I don’t know any of the details of how the naming gets resolved, I didn’t see where the load put that module into the panda3d package. Just haven’t read through it enough.

Trying your suggestion… almost the same result “ModuleNotFoundError: No module named ‘panda3d.core’” – slight progress?

I didn’t mean to suggest that you didn’t know panda3d was a package; it’s the Python interpreter that evidently isn’t yet convinced of it. :wink: Evidently, Python 3 requires a __package__ member to be set on every module that is supposed to be a package.

Because we link in and initialize core.bc directly, we need to manually set up “panda3d” as a package, and then we attach the loaded “core” module into the module dictionary of the “panda3d” module, which is what the code in loadPanda is meant to do.

OK, then at least my thinking was logically correct. That is what I thought had to be done. And apparently the last step, loading core into the panda3d package, is still not accomplished?

[Side minor note, we have core.so not core.bc, but I assume same result]

After we get core inserted, we will have to do the same with direct and physics?

That’s what this line is supposed to do:

PyDict_SetItemString(panda3d_dict, "core", PyImport_ImportModule("panda3d.core"));

However, it’s failing at the PyImport_ImportModule line, because that is stopping when it thinks “panda3d” isn’t a package.

Actually, since PyInit nowadays returns the module object itself, let’s try this instead, which avoids PyImport_ImportModule altogether:

EMSCRIPTEN_KEEPALIVE void loadPanda() {
  PyObject *panda3d_module = PyImport_AddModule("panda3d");
  PyModule_AddStringConstant(panda3d_module, "__package__", "panda3d");
  PyModule_AddObject(panda3d_module, "__path__", PyList_New(0));

  PyObject *core_module = PyInit_core();

  PyObject *panda3d_dict = PyModule_GetDict(panda3d_module);
  PyDict_SetItemString(panda3d_dict, "core", core_module);

Once you get panda3d.core working, it may indeed be prudent to do the same with direct and other modules that you might need. Just add the following, then:

  PyObject *direct_module = PyInit_direct();
  PyDict_SetItemString(panda3d_dict, "direct", direct_module);

warning: incompatible integer to pointer conversion initializing ‘PyObject *’ (aka ‘struct _object *’) with an expression of type ‘int’ [-Wint-conversion]
PyObject *core_module = PyInit_core();

But just a warning, continuing build…

Ah, you should fix that; you need to update the extern declaration to have a return type of PyObject * instead of int.

Never mind. My typos…

In your editor.c, you have this:

extern int PyInit_core();

I assume that’s coming from your freezify.py; just update it to say

extern PyObject *PyInit_core();