From c5477429a310f3c90816cfd8daee74eb12131c52 Mon Sep 17 00:00:00 2001 From: tz12323 <2760686250@qq.com> Date: Tue, 13 Oct 2026 19:16:12 +0800 Subject: [PATCH 1/5] feat: add pybind11 package Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- pkgs/p/pybind.pybind11.lua | 51 ++++++++++ tests/examples/pybind11/build.mcpp | 124 ++++++++++++++++++++++++ tests/examples/pybind11/mcpp.toml | 9 ++ tests/examples/pybind11/tests/embed.cpp | 5 + 4 files changed, 189 insertions(+) create mode 100644 pkgs/p/pybind.pybind11.lua create mode 100644 tests/examples/pybind11/build.mcpp create mode 100644 tests/examples/pybind11/mcpp.toml create mode 100644 tests/examples/pybind11/tests/embed.cpp diff --git a/pkgs/p/pybind.pybind11.lua b/pkgs/p/pybind.pybind11.lua new file mode 100644 index 00000000..f00a8139 --- /dev/null +++ b/pkgs/p/pybind.pybind11.lua @@ -0,0 +1,51 @@ +-- Form B inline descriptor for pybind11, a header-only library for creating +-- Python bindings from C++. +-- +-- Python's headers and import library are supplied by the consumer's Python +-- installation. The companion build.mcpp test demonstrates how to discover +-- those platform-specific paths and add them to an mcpp build. +package = { + spec = "1", + namespace = "compat", + name = "pybind11", + description = "Seamless operability between C++11 and Python", + licenses = {"BSD-3-Clause"}, + repo = "https://github.com/pybind/pybind11", + type = "package", + + xpm = { + linux = { + ["3.2.0"] = { + url = "https://github.com/pybind/pybind11/archive/6254086d96062249c5153c17eab6b4437dd1589f.tar.gz", + sha256 = "4a77778750ae4bd27d29dd328902b03a7efb9c3468c6f23180fa39d80c05f635", + }, + }, + macosx = { + ["3.2.0"] = { + url = "https://github.com/pybind/pybind11/archive/6254086d96062249c5153c17eab6b4437dd1589f.tar.gz", + sha256 = "4a77778750ae4bd27d29dd328902b03a7efb9c3468c6f23180fa39d80c05f635", + }, + }, + windows = { + ["3.2.0"] = { + url = "https://github.com/pybind/pybind11/archive/6254086d96062249c5153c17eab6b4437dd1589f.tar.gz", + sha256 = "4a77778750ae4bd27d29dd328902b03a7efb9c3468c6f23180fa39d80c05f635", + }, + }, + }, + + mcpp = { + language = "c++23", + import_std = false, + c_standard = "c11", + include_dirs = { "*/include" }, + generated_files = { + ["mcpp_generated/pybind11_anchor.c"] = [==[ +int mcpp_pybind11_headers_anchor(void) { return 0; } +]==], + }, + sources = { "mcpp_generated/pybind11_anchor.c" }, + targets = { ["pybind11"] = { kind = "lib" } }, + deps = {}, + }, +} diff --git a/tests/examples/pybind11/build.mcpp b/tests/examples/pybind11/build.mcpp new file mode 100644 index 00000000..cf3a5ca2 --- /dev/null +++ b/tests/examples/pybind11/build.mcpp @@ -0,0 +1,124 @@ +#include +#include +#include +#include +#include +#include + +import mcpp; + +#ifdef _WIN32 +extern "C" FILE* _popen(const char*, const char*); +extern "C" int _pclose(FILE*); +#else +#include +#endif + +namespace { + +FILE* open_pipe(const char* command) { +#ifdef _WIN32 + return _popen(command, "r"); +#else + return popen(command, "r"); +#endif +} + +int close_pipe(FILE* pipe) { +#ifdef _WIN32 + return _pclose(pipe); +#else + return pclose(pipe); +#endif +} + +bool read_line(FILE* pipe, std::string& value) { + char buffer[4096]; + if (std::fgets(buffer, sizeof(buffer), pipe) == nullptr) return false; + value = buffer; + while (!value.empty() && (value.back() == '\n' || value.back() == '\r')) + value.pop_back(); + return true; +} + +std::string python_executable() { + return std::string(mcpp::target_os()) == "windows" ? "py -3" : "python3"; +} + +std::string python_info_command() { + return python_executable() + + R"PY( -c "import sysconfig; print(sysconfig.get_path('include')); print(sysconfig.get_config_var('LIBDIR') or sysconfig.get_config_var('prefix') or ''); print(sysconfig.get_config_var('LIBPL') or ''); print(sysconfig.get_config_var('LDLIBRARY') or ''); print(sysconfig.get_config_var('LIBRARY') or ''); print((sysconfig.get_config_var('LIBS') or '') + ' ' + (sysconfig.get_config_var('SYSLIBS') or ''))")PY"; +} + +std::string library_name(std::string library) { + const auto slash = library.find_last_of("/\\"); + if (slash != std::string::npos) library.erase(0, slash + 1); + if (library.rfind("lib", 0) == 0) library.erase(0, 3); + for (const std::string suffix : {".so", ".dylib", ".a", ".lib"}) { + const auto dot = library.find(suffix); + if (dot != std::string::npos) { + library.erase(dot); + break; + } + } + return library; +} + +} // namespace + +int main() { + FILE* pipe = open_pipe(python_info_command().c_str()); + if (pipe == nullptr) { + std::fputs("unable to start the platform Python command\n", stderr); + return 1; + } + + std::string include_dir; + std::string shared_library_dir; + std::string static_library_dir; + std::string shared_library_file; + std::string static_library_file; + std::string system_libraries; + const bool read_ok = read_line(pipe, include_dir) && + read_line(pipe, shared_library_dir) && + read_line(pipe, static_library_dir) && + read_line(pipe, shared_library_file) && + read_line(pipe, static_library_file) && + read_line(pipe, system_libraries); + const int exit_code = close_pipe(pipe); + if (!read_ok || exit_code != 0 || include_dir.empty() || + shared_library_dir.empty() || shared_library_file.empty()) { + std::fputs("Python did not report usable include/library paths\n", stderr); + return 1; + } + + std::string library_dir = shared_library_dir; + std::string library_file = shared_library_file; + bool static_library = false; + if (std::string(mcpp::target_os()) != "windows" && + !static_library_dir.empty() && !static_library_file.empty() && + std::filesystem::exists( + std::filesystem::path(static_library_dir) / static_library_file)) { + library_dir = static_library_dir; + library_file = static_library_file; + static_library = true; + } + + mcpp::include_dir(include_dir.c_str()); + mcpp::link_search(library_dir.c_str()); + mcpp::runtime_search_dir(library_dir.c_str()); + if (library_dir != shared_library_dir) + mcpp::runtime_search_dir(shared_library_dir.c_str()); + const std::string link_name = + static_library ? ":" + library_file : library_name(library_file); + mcpp::link_lib(link_name.c_str()); + + std::istringstream libraries(system_libraries); + for (std::string flag; libraries >> flag;) { + if (flag.rfind("-l", 0) == 0 && flag.size() > 2) + mcpp::link_lib(flag.c_str() + 2); + } + + mcpp::rerun_if_changed("build.mcpp"); + return 0; +} diff --git a/tests/examples/pybind11/mcpp.toml b/tests/examples/pybind11/mcpp.toml new file mode 100644 index 00000000..a3f9fcf8 --- /dev/null +++ b/tests/examples/pybind11/mcpp.toml @@ -0,0 +1,9 @@ +[package] +name = "pybind11-tests" +version = "0.1.0" + +[dependencies.compat] +pybind11 = "3.2.0" + +[indices] +compat = { path = "../../.." } diff --git a/tests/examples/pybind11/tests/embed.cpp b/tests/examples/pybind11/tests/embed.cpp new file mode 100644 index 00000000..d3a8c304 --- /dev/null +++ b/tests/examples/pybind11/tests/embed.cpp @@ -0,0 +1,5 @@ +#include + +int main() { + return PYBIND11_VERSION_MAJOR == 3 ? 0 : 1; +} From 3d1dc7d7769aa0e1ff0c6fe73ca0e3d8406f7029 Mon Sep 17 00:00:00 2001 From: tz12323 <2760686250@qq.com> Date: Tue, 13 Oct 2026 19:24:39 +0800 Subject: [PATCH 2/5] Add pybind11 example to mcpp.toml --- mcpp.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/mcpp.toml b/mcpp.toml index a96c9edc..31fbce32 100644 --- a/mcpp.toml +++ b/mcpp.toml @@ -184,6 +184,7 @@ members = [ "tests/examples/nanosvg", "tests/examples/nanodbc", "tests/examples/plf-hive", + "tests/examples/pybind11", "tests/examples/spirv-reflect", "tests/examples/vulkan-memory-allocator", "tests/examples/wamr", From ce58cf815a614fc6b65a5e563549003112ee8b40 Mon Sep 17 00:00:00 2001 From: tz12323 <2760686250@qq.com> Date: Tue, 13 Oct 2026 19:30:13 +0800 Subject: [PATCH 3/5] fix pybind11 Python platform configuration Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- tests/examples/pybind11/build.mcpp | 27 ++++++++++++++++++++----- tests/examples/pybind11/tests/embed.cpp | 2 +- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/tests/examples/pybind11/build.mcpp b/tests/examples/pybind11/build.mcpp index cf3a5ca2..8a9e6b42 100644 --- a/tests/examples/pybind11/build.mcpp +++ b/tests/examples/pybind11/build.mcpp @@ -47,7 +47,7 @@ std::string python_executable() { std::string python_info_command() { return python_executable() + - R"PY( -c "import sysconfig; print(sysconfig.get_path('include')); print(sysconfig.get_config_var('LIBDIR') or sysconfig.get_config_var('prefix') or ''); print(sysconfig.get_config_var('LIBPL') or ''); print(sysconfig.get_config_var('LDLIBRARY') or ''); print(sysconfig.get_config_var('LIBRARY') or ''); print((sysconfig.get_config_var('LIBS') or '') + ' ' + (sysconfig.get_config_var('SYSLIBS') or ''))")PY"; + R"PY( -c "import sysconfig; print(sysconfig.get_path('include')); print(sysconfig.get_config_var('LIBDIR') or sysconfig.get_config_var('prefix') or ''); print(sysconfig.get_config_var('LIBPL') or ''); print(sysconfig.get_config_var('LDLIBRARY') or ''); print(sysconfig.get_config_var('LIBRARY') or ''); print((sysconfig.get_config_var('LIBS') or '') + ' ' + (sysconfig.get_config_var('SYSLIBS') or '')); print(sysconfig.get_config_var('SIZEOF_LONG') or ''); print(sysconfig.get_config_var('SIZEOF_WCHAR_T') or ''); print('1' if sysconfig.get_config_var('WITH_THREAD') else '')")PY"; } std::string library_name(std::string library) { @@ -79,12 +79,18 @@ int main() { std::string shared_library_file; std::string static_library_file; std::string system_libraries; + std::string sizeof_long; + std::string sizeof_wchar_t; + std::string with_thread; const bool read_ok = read_line(pipe, include_dir) && read_line(pipe, shared_library_dir) && read_line(pipe, static_library_dir) && read_line(pipe, shared_library_file) && read_line(pipe, static_library_file) && - read_line(pipe, system_libraries); + read_line(pipe, system_libraries) && + read_line(pipe, sizeof_long) && + read_line(pipe, sizeof_wchar_t) && + read_line(pipe, with_thread); const int exit_code = close_pipe(pipe); if (!read_ok || exit_code != 0 || include_dir.empty() || shared_library_dir.empty() || shared_library_file.empty()) { @@ -95,8 +101,7 @@ int main() { std::string library_dir = shared_library_dir; std::string library_file = shared_library_file; bool static_library = false; - if (std::string(mcpp::target_os()) != "windows" && - !static_library_dir.empty() && !static_library_file.empty() && + if (!static_library_dir.empty() && !static_library_file.empty() && std::filesystem::exists( std::filesystem::path(static_library_dir) / static_library_file)) { library_dir = static_library_dir; @@ -109,10 +114,22 @@ int main() { mcpp::runtime_search_dir(library_dir.c_str()); if (library_dir != shared_library_dir) mcpp::runtime_search_dir(shared_library_dir.c_str()); + const bool windows = std::string(mcpp::target_os()) == "windows"; + const bool exact_static = + static_library && !windows && std::string(mcpp::target_os()) != "macosx"; const std::string link_name = - static_library ? ":" + library_file : library_name(library_file); + exact_static ? ":" + library_file : library_name(library_file); mcpp::link_lib(link_name.c_str()); + if (!sizeof_long.empty()) + mcpp::cxxflag(("-DSIZEOF_LONG=" + sizeof_long).c_str()); + if (!sizeof_wchar_t.empty()) + mcpp::cxxflag(("-DSIZEOF_WCHAR_T=" + sizeof_wchar_t).c_str()); + if (!with_thread.empty()) + mcpp::cxxflag("-DWITH_THREAD=1"); + if (!windows && !sizeof_long.empty()) + mcpp::cxxflag(("-DLONG_BIT=" + std::to_string(std::stol(sizeof_long) * 8)).c_str()); + std::istringstream libraries(system_libraries); for (std::string flag; libraries >> flag;) { if (flag.rfind("-l", 0) == 0 && flag.size() > 2) diff --git a/tests/examples/pybind11/tests/embed.cpp b/tests/examples/pybind11/tests/embed.cpp index d3a8c304..e08f35a3 100644 --- a/tests/examples/pybind11/tests/embed.cpp +++ b/tests/examples/pybind11/tests/embed.cpp @@ -1,4 +1,4 @@ -#include +#include int main() { return PYBIND11_VERSION_MAJOR == 3 ? 0 : 1; From c6523d606c337ea8043ed4517cad964a510861df Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Tue, 13 Oct 2026 21:53:19 +0900 Subject: [PATCH 4/5] fix(pybind11): use matching Python SDK and exercise embedded bindings --- .agents/docs/2026-10-13-add-pybind11-plan.md | 22 +++ README.md | 2 + README.zh-CN.md | 2 + .../compat.pybind11.lua} | 11 +- tests/examples/pybind11/build.mcpp | 149 +++--------------- tests/examples/pybind11/mcpp.toml | 5 +- tests/examples/pybind11/tests/embed.cpp | 19 ++- 7 files changed, 74 insertions(+), 136 deletions(-) create mode 100644 .agents/docs/2026-10-13-add-pybind11-plan.md rename pkgs/{p/pybind.pybind11.lua => c/compat.pybind11.lua} (84%) diff --git a/.agents/docs/2026-10-13-add-pybind11-plan.md b/.agents/docs/2026-10-13-add-pybind11-plan.md new file mode 100644 index 00000000..fcd9f3b0 --- /dev/null +++ b/.agents/docs/2026-10-13-add-pybind11-plan.md @@ -0,0 +1,22 @@ +# Add compat.pybind11 + +The upstream revision 6254086d96062249c5153c17eab6b4437dd1589f identifies +itself as 3.2.0a0, not released 3.2.0. The index version is therefore +3.2.0-dev.6254086. Its archive hash was checked twice; license BSD-3-Clause. + +Form B: original header-only include/ plus a generated C anchor. The descriptor +identity and path are compat.pybind11 and pkgs/c/compat.pybind11.lua. No optional +compiled sources exist, so there are no package features. Plain-string GLOBAL +URLs retain the documented CN fallback. + +Python is a consumer SDK dependency, not vendored into pybind11. The example +declares xim:python 3.13.12 and asks mcpp::xpkg_dir for its actual payload. +Headers, library and standard library all come from that SDK, avoiding system +Python multiarch configuration and mixed-libc failures. It selects python313.lib +on Windows and python3.13 on Unix; Windows deploys the matching Python DLL. +It never invents Python configuration macros or uses Linux-only link syntax. + +The example embeds Python, evaluates a sum and calls a C++ function from +Python, asserting both results. Local validation with CI-pinned mcpp +2026.10.1.2 and gcc 16.1.0: `mcpp test -p pybind11`: +`test result ok. 1 passed; 0 failed`. diff --git a/README.md b/README.md index 0459ca25..59ba0878 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,8 @@ the online index; open a PR and confirm CI is green. A PR runs lint and tests only the workspace members that depend on the changed descriptors; after the merge, `deploy-site` publishes the site. +Additional example: [`compat.pybind11`](pkgs/c/compat.pybind11.lua) — Header-only Python bindings; [`consumer test`](tests/examples/pybind11/mcpp.toml). + ## Documentation - [Library shapes and descriptor templates](docs/package-types.md) diff --git a/README.zh-CN.md b/README.zh-CN.md index a996bda4..00a96142 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -59,6 +59,8 @@ agent(如 Claude Code)即可: 提交 PR 后,CI 执行 lint,并只测试依赖了被修改描述符的 workspace 成员;合并后,`deploy-site` 发布站点。 +新增示例: [`compat.pybind11`](pkgs/c/compat.pybind11.lua) — Header-only Python bindings; [`消费测试`](tests/examples/pybind11/mcpp.toml)。 + ## 文档 - [库形态与描述符模板](docs/zh/package-types.md) diff --git a/pkgs/p/pybind.pybind11.lua b/pkgs/c/compat.pybind11.lua similarity index 84% rename from pkgs/p/pybind.pybind11.lua rename to pkgs/c/compat.pybind11.lua index f00a8139..4b2a6cb4 100644 --- a/pkgs/p/pybind.pybind11.lua +++ b/pkgs/c/compat.pybind11.lua @@ -1,9 +1,8 @@ -- Form B inline descriptor for pybind11, a header-only library for creating -- Python bindings from C++. -- --- Python's headers and import library are supplied by the consumer's Python --- installation. The companion build.mcpp test demonstrates how to discover --- those platform-specific paths and add them to an mcpp build. +-- Python headers and libraries come from a matching consumer Python SDK. +-- The example declares xim:python and uses its payload rather than host Python. package = { spec = "1", namespace = "compat", @@ -15,19 +14,19 @@ package = { xpm = { linux = { - ["3.2.0"] = { + ["3.2.0-dev.6254086"] = { url = "https://github.com/pybind/pybind11/archive/6254086d96062249c5153c17eab6b4437dd1589f.tar.gz", sha256 = "4a77778750ae4bd27d29dd328902b03a7efb9c3468c6f23180fa39d80c05f635", }, }, macosx = { - ["3.2.0"] = { + ["3.2.0-dev.6254086"] = { url = "https://github.com/pybind/pybind11/archive/6254086d96062249c5153c17eab6b4437dd1589f.tar.gz", sha256 = "4a77778750ae4bd27d29dd328902b03a7efb9c3468c6f23180fa39d80c05f635", }, }, windows = { - ["3.2.0"] = { + ["3.2.0-dev.6254086"] = { url = "https://github.com/pybind/pybind11/archive/6254086d96062249c5153c17eab6b4437dd1589f.tar.gz", sha256 = "4a77778750ae4bd27d29dd328902b03a7efb9c3468c6f23180fa39d80c05f635", }, diff --git a/tests/examples/pybind11/build.mcpp b/tests/examples/pybind11/build.mcpp index 8a9e6b42..ffbf6f7d 100644 --- a/tests/examples/pybind11/build.mcpp +++ b/tests/examples/pybind11/build.mcpp @@ -1,141 +1,36 @@ #include -#include #include -#include +#include #include -#include import mcpp; -#ifdef _WIN32 -extern "C" FILE* _popen(const char*, const char*); -extern "C" int _pclose(FILE*); -#else -#include -#endif - -namespace { - -FILE* open_pipe(const char* command) { -#ifdef _WIN32 - return _popen(command, "r"); -#else - return popen(command, "r"); -#endif -} - -int close_pipe(FILE* pipe) { -#ifdef _WIN32 - return _pclose(pipe); -#else - return pclose(pipe); -#endif -} - -bool read_line(FILE* pipe, std::string& value) { - char buffer[4096]; - if (std::fgets(buffer, sizeof(buffer), pipe) == nullptr) return false; - value = buffer; - while (!value.empty() && (value.back() == '\n' || value.back() == '\r')) - value.pop_back(); - return true; -} - -std::string python_executable() { - return std::string(mcpp::target_os()) == "windows" ? "py -3" : "python3"; -} - -std::string python_info_command() { - return python_executable() + - R"PY( -c "import sysconfig; print(sysconfig.get_path('include')); print(sysconfig.get_config_var('LIBDIR') or sysconfig.get_config_var('prefix') or ''); print(sysconfig.get_config_var('LIBPL') or ''); print(sysconfig.get_config_var('LDLIBRARY') or ''); print(sysconfig.get_config_var('LIBRARY') or ''); print((sysconfig.get_config_var('LIBS') or '') + ' ' + (sysconfig.get_config_var('SYSLIBS') or '')); print(sysconfig.get_config_var('SIZEOF_LONG') or ''); print(sysconfig.get_config_var('SIZEOF_WCHAR_T') or ''); print('1' if sysconfig.get_config_var('WITH_THREAD') else '')")PY"; -} - -std::string library_name(std::string library) { - const auto slash = library.find_last_of("/\\"); - if (slash != std::string::npos) library.erase(0, slash + 1); - if (library.rfind("lib", 0) == 0) library.erase(0, 3); - for (const std::string suffix : {".so", ".dylib", ".a", ".lib"}) { - const auto dot = library.find(suffix); - if (dot != std::string::npos) { - library.erase(dot); - break; - } - } - return library; -} - -} // namespace - int main() { - FILE* pipe = open_pipe(python_info_command().c_str()); - if (pipe == nullptr) { - std::fputs("unable to start the platform Python command\n", stderr); - return 1; - } - - std::string include_dir; - std::string shared_library_dir; - std::string static_library_dir; - std::string shared_library_file; - std::string static_library_file; - std::string system_libraries; - std::string sizeof_long; - std::string sizeof_wchar_t; - std::string with_thread; - const bool read_ok = read_line(pipe, include_dir) && - read_line(pipe, shared_library_dir) && - read_line(pipe, static_library_dir) && - read_line(pipe, shared_library_file) && - read_line(pipe, static_library_file) && - read_line(pipe, system_libraries) && - read_line(pipe, sizeof_long) && - read_line(pipe, sizeof_wchar_t) && - read_line(pipe, with_thread); - const int exit_code = close_pipe(pipe); - if (!read_ok || exit_code != 0 || include_dir.empty() || - shared_library_dir.empty() || shared_library_file.empty()) { - std::fputs("Python did not report usable include/library paths\n", stderr); + namespace fs = std::filesystem; + const fs::path python = mcpp::xpkg_dir("xim", "python"); + if (python.empty() || !fs::is_directory(python)) { + std::fputs("the declared xim:python SDK is unavailable\n", stderr); return 1; } - - std::string library_dir = shared_library_dir; - std::string library_file = shared_library_file; - bool static_library = false; - if (!static_library_dir.empty() && !static_library_file.empty() && - std::filesystem::exists( - std::filesystem::path(static_library_dir) / static_library_file)) { - library_dir = static_library_dir; - library_file = static_library_file; - static_library = true; - } - - mcpp::include_dir(include_dir.c_str()); - mcpp::link_search(library_dir.c_str()); - mcpp::runtime_search_dir(library_dir.c_str()); - if (library_dir != shared_library_dir) - mcpp::runtime_search_dir(shared_library_dir.c_str()); const bool windows = std::string(mcpp::target_os()) == "windows"; - const bool exact_static = - static_library && !windows && std::string(mcpp::target_os()) != "macosx"; - const std::string link_name = - exact_static ? ":" + library_file : library_name(library_file); - mcpp::link_lib(link_name.c_str()); - - if (!sizeof_long.empty()) - mcpp::cxxflag(("-DSIZEOF_LONG=" + sizeof_long).c_str()); - if (!sizeof_wchar_t.empty()) - mcpp::cxxflag(("-DSIZEOF_WCHAR_T=" + sizeof_wchar_t).c_str()); - if (!with_thread.empty()) - mcpp::cxxflag("-DWITH_THREAD=1"); - if (!windows && !sizeof_long.empty()) - mcpp::cxxflag(("-DLONG_BIT=" + std::to_string(std::stol(sizeof_long) * 8)).c_str()); - - std::istringstream libraries(system_libraries); - for (std::string flag; libraries >> flag;) { - if (flag.rfind("-l", 0) == 0 && flag.size() > 2) - mcpp::link_lib(flag.c_str() + 2); + const fs::path headers = python / "include" / (windows ? "" : "python3.13"); + const fs::path libraries = python / (windows ? "libs" : "lib"); + if (!fs::exists(headers / "Python.h")) return 1; + mcpp::include_dir(headers.string().c_str()); + mcpp::link_search(libraries.string().c_str()); + mcpp::link_lib(windows ? "python313" : "python3.13"); + mcpp::runtime_search_dir((python / (windows ? "" : "lib")).string().c_str()); + // Resolve the standard library in the same SDK when embedding Python. + const fs::path config_dir = fs::absolute(".mcpp/python-sdk"); + fs::create_directories(config_dir); + std::ofstream config(config_dir / "python_sdk.h"); + config << "#define MCPP_PYTHON_HOME R\"SDK(" << python.generic_string() << ")SDK\"\n"; + config.close(); + if (!config) return 1; + mcpp::include_dir(config_dir.string().c_str()); + if (windows) { + mcpp::deploy((python / "python313.dll").string().c_str(), "python313.dll"); } - mcpp::rerun_if_changed("build.mcpp"); return 0; } diff --git a/tests/examples/pybind11/mcpp.toml b/tests/examples/pybind11/mcpp.toml index a3f9fcf8..5c5e4931 100644 --- a/tests/examples/pybind11/mcpp.toml +++ b/tests/examples/pybind11/mcpp.toml @@ -3,7 +3,10 @@ name = "pybind11-tests" version = "0.1.0" [dependencies.compat] -pybind11 = "3.2.0" +pybind11 = "3.2.0-dev.6254086" [indices] compat = { path = "../../.." } + +[xlings.workspace] +"xim:python" = "3.13.12" diff --git a/tests/examples/pybind11/tests/embed.cpp b/tests/examples/pybind11/tests/embed.cpp index e08f35a3..7a7e0308 100644 --- a/tests/examples/pybind11/tests/embed.cpp +++ b/tests/examples/pybind11/tests/embed.cpp @@ -1,5 +1,20 @@ -#include +#include +#include int main() { - return PYBIND11_VERSION_MAJOR == 3 ? 0 : 1; + namespace py = pybind11; + PyConfig config; + PyConfig_InitPythonConfig(&config); + const PyStatus status = PyConfig_SetBytesString(&config, &config.home, MCPP_PYTHON_HOME); + if (PyStatus_Exception(status)) { + PyConfig_Clear(&config); + return 1; + } + py::scoped_interpreter interpreter(&config); + PyConfig_Clear(&config); + const int answer = py::eval("sum([1, 2, 3, 4])").cast(); + py::dict globals; + globals["double_value"] = py::cpp_function([](int value) { return 2 * value; }); + py::exec("result = double_value(21)", globals); + return answer == 10 && globals["result"].cast() == 42 ? 0 : 2; } From eacdbc36a0f66743434895b886e20153d3b78b35 Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Tue, 13 Oct 2026 21:56:43 +0900 Subject: [PATCH 5/5] fix(pybind11): let runtime discovery stage the SDK DLLs once --- .agents/docs/2026-10-13-add-pybind11-plan.md | 2 +- tests/examples/pybind11/build.mcpp | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/.agents/docs/2026-10-13-add-pybind11-plan.md b/.agents/docs/2026-10-13-add-pybind11-plan.md index fcd9f3b0..2b77a2a0 100644 --- a/.agents/docs/2026-10-13-add-pybind11-plan.md +++ b/.agents/docs/2026-10-13-add-pybind11-plan.md @@ -13,7 +13,7 @@ Python is a consumer SDK dependency, not vendored into pybind11. The example declares xim:python 3.13.12 and asks mcpp::xpkg_dir for its actual payload. Headers, library and standard library all come from that SDK, avoiding system Python multiarch configuration and mixed-libc failures. It selects python313.lib -on Windows and python3.13 on Unix; Windows deploys the matching Python DLL. +on Windows and python3.13 on Unix; The SDK runtime directory lets mcpp deploy the matching Python DLLs on Windows. It never invents Python configuration macros or uses Linux-only link syntax. The example embeds Python, evaluates a sum and calls a C++ function from diff --git a/tests/examples/pybind11/build.mcpp b/tests/examples/pybind11/build.mcpp index ffbf6f7d..91220515 100644 --- a/tests/examples/pybind11/build.mcpp +++ b/tests/examples/pybind11/build.mcpp @@ -28,9 +28,6 @@ int main() { config.close(); if (!config) return 1; mcpp::include_dir(config_dir.string().c_str()); - if (windows) { - mcpp::deploy((python / "python313.dll").string().c_str(), "python313.dll"); - } mcpp::rerun_if_changed("build.mcpp"); return 0; }