CPython opens the stdlib zip with its own stdio, so it needs a real path.
On the desktop res:// is a directory and globalize_path() is enough; in an
exported build res:// is inside the PCK, which nothing outside Godot reads.
make_stdlib_zip.py now also writes <out>.sha256
mtpython_stdlib_project copies python27.zip + .sha256 into project/
(gitignored), and both export presets' include_filter lists them, so
they enter the PCK — checked with --export-pack
extension/src/python_stdlib.{h,cpp} (Metin2Python.stdlib_path in
GDScript): reads the bytes out of the PCK, writes
user://python27.zip.part, hashes the file as written against the
shipped digest, then renames. A sandbox copy is reused only when it
matches, so a first start killed mid-copy and a zip replaced by a new
build both re-stage instead of feeding zipimport a torn file.
PythonHost::SetDefaultStdLibPath / DefaultStdLibPath() answer with that
path ($MT_PYTHON_STDLIB still wins). python_stdlib.cpp is the only unit
that knows res:// / user://; port_platform stays godot-free.
Also fixes the Windows gate, which step 3b broke: without mtpython the
MinGW build compiled UserInterface/StdAfx.h (it includes ScriptLib/StdAfx.h,
as the original PCH does). port/CMakeLists.txt excludes that header and
PythonPackModule.cpp with ScriptLib, and platform/CMakeLists.txt excludes
platform/ScriptLib/ the same way.
Not done: on-device staging. The macOS test drives the mobile path with
force_stage=true, and nothing in the app boot calls it yet — the
interpreter only starts in the process with 2V0.
gates: python_stdlib_test.gd PASS (in-place path, staging, sha256, bytes
equal res://, no .part, ZIPReader finds encodings/__init__.py, no re-copy
on a second call, corrupted copy re-staged) · macOS ctest 27/27 ·
mingw + android + ios port_platform compile clean · port_map.py check 0
errors · key leak check 8/8 none.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
38 lines
1.7 KiB
C++
38 lines
1.7 KiB
C++
#pragma once
|
|
// python_stdlib — where the embedded interpreter's standard library is on this platform
|
|
// (docs/PORT-PLAN.md 批次 2P step 3c).
|
|
//
|
|
// CPython opens python27.zip with its own stdio, so it needs a real filesystem path. That is free on
|
|
// the desktop, where res:// is a directory on disk, but on Android and iOS res:// is inside the PCK,
|
|
// which nothing outside Godot can read. There the zip is copied once into the sandbox (user://) and
|
|
// the copy is accepted only when it hashes to the digest built beside the zip, so an interrupted
|
|
// first start or a zip replaced by a new build re-stages instead of feeding zipimport a torn file.
|
|
//
|
|
// Only this file knows about res:// / user://: PythonHost (platform/ScriptLib) stays godot-free and
|
|
// is told the path, through PythonHost::SetDefaultStdLibPath.
|
|
|
|
#include <godot_cpp/classes/object.hpp>
|
|
#include <godot_cpp/variant/string.hpp>
|
|
|
|
namespace mtgodot {
|
|
|
|
// The real filesystem path of python27.zip, staging it out of res:// when res:// is not on disk.
|
|
// Empty when the zip is missing or the staged copy does not verify; the reason goes to *error.
|
|
// force_stage runs the mobile path even where res:// is readable, which is how it is tested.
|
|
godot::String stage_python_stdlib(godot::String *error = nullptr, bool force_stage = false);
|
|
|
|
class Metin2Python : public godot::Object {
|
|
GDCLASS(Metin2Python, godot::Object)
|
|
|
|
public:
|
|
// Returns the path and, when the embedded interpreter is built in, hands it to PythonHost so
|
|
// PythonHost::DefaultStdLibPath() answers with it from then on.
|
|
static godot::String stdlib_path(bool force_stage);
|
|
static godot::String last_error();
|
|
|
|
protected:
|
|
static void _bind_methods();
|
|
};
|
|
|
|
} // namespace mtgodot
|