From 03b0c2d2dd423c97a50ece674e750f6873c9ea1f Mon Sep 17 00:00:00 2001 From: wellwei Date: Sat, 8 Aug 2026 18:11:59 +0800 Subject: [PATCH 1/9] feat: add libmysqlclient and MySQL Connector/C++ --- mcpp.toml | 2 + pkgs/c/compat.libmysqlclient.lua | 27 ++ pkgs/c/compat.mysql-connector-cpp.lua | 239 ++++++++++++++++++ tests/examples/libmysqlclient/mcpp.toml | 16 ++ .../examples/libmysqlclient/tests/client.cpp | 15 ++ tests/examples/mysql-connector-cpp/mcpp.toml | 17 ++ .../mysql-connector-cpp/tests/connector.cpp | 20 ++ 7 files changed, 336 insertions(+) create mode 100644 pkgs/c/compat.libmysqlclient.lua create mode 100644 pkgs/c/compat.mysql-connector-cpp.lua create mode 100644 tests/examples/libmysqlclient/mcpp.toml create mode 100644 tests/examples/libmysqlclient/tests/client.cpp create mode 100644 tests/examples/mysql-connector-cpp/mcpp.toml create mode 100644 tests/examples/mysql-connector-cpp/tests/connector.cpp diff --git a/mcpp.toml b/mcpp.toml index 00f6f09..4bfe121 100644 --- a/mcpp.toml +++ b/mcpp.toml @@ -44,6 +44,8 @@ members = [ "tests/examples/imgui-module", "tests/examples/imgui-window", "tests/examples/marzer.tomlplusplus", + "tests/examples/libmysqlclient", + "tests/examples/mysql-connector-cpp", "tests/examples/nlohmann.json", "tests/examples/openblas", "tests/examples/openssl", diff --git a/pkgs/c/compat.libmysqlclient.lua b/pkgs/c/compat.libmysqlclient.lua new file mode 100644 index 0000000..65da1f2 --- /dev/null +++ b/pkgs/c/compat.libmysqlclient.lua @@ -0,0 +1,27 @@ +-- Form A: 发布归档自带根级 mcpp.toml,消费者直接用 mcpp 构建客户端静态库。 +package = { + spec = "1", + namespace = "compat", + name = "libmysqlclient", + description = "MySQL C API client library (static, client-only source)", + licenses = {"GPL-2.0-only", "Universal-FOSS-exception-1.0"}, + repo = "https://github.com/wellwei/libmysqlclient", + type = "package", + + xpm = { + linux = { + ["8.4.6"] = { + url = "https://github.com/wellwei/libmysqlclient/archive/refs/tags/8.4.6.tar.gz", + sha256 = "de2b7c42072e83005cff62c2506959865d923b31599f75a9d33521be99cf9e8d", + }, + }, + macosx = { + ["8.4.6"] = { + url = "https://github.com/wellwei/libmysqlclient/archive/refs/tags/8.4.6.tar.gz", + sha256 = "de2b7c42072e83005cff62c2506959865d923b31599f75a9d33521be99cf9e8d", + }, + }, + }, + + -- 无 `mcpp` 字段:默认查找会命中归档根目录的 mcpp.toml。 +} diff --git a/pkgs/c/compat.mysql-connector-cpp.lua b/pkgs/c/compat.mysql-connector-cpp.lua new file mode 100644 index 0000000..d6e2c05 --- /dev/null +++ b/pkgs/c/compat.mysql-connector-cpp.lua @@ -0,0 +1,239 @@ +-- compat.mysql-connector-cpp -- MySQL Connector/C++ X DevAPI and JDBC. +package = { + spec = "1", + namespace = "compat", + name = "mysql-connector-cpp", + description = "MySQL Connector/C++ X DevAPI and JDBC (static, source-built)", + licenses = {"GPL-2.0-only", "Universal-FOSS-exception-1.0"}, + repo = "https://github.com/mysql/mysql-connector-cpp", + type = "package", + + xpm = { + linux = { + deps = { "xim:cmake@latest", "xim:make@latest" }, + ["26.7.0"] = { + url = "https://github.com/mysql/mysql-connector-cpp/archive/refs/tags/26.7.0.tar.gz", + sha256 = "b2299862eefc33fd71c0aac68328305671805fc955e6bd2578ef205c10f98550", + }, + }, + macosx = { + deps = { "xim:cmake@latest" }, + ["26.7.0"] = { + url = "https://github.com/mysql/mysql-connector-cpp/archive/refs/tags/26.7.0.tar.gz", + sha256 = "b2299862eefc33fd71c0aac68328305671805fc955e6bd2578ef205c10f98550", + }, + }, + }, + + mcpp = { + language = "c++23", + import_std = false, + sources = { "mcpp_mysql_connector_cpp_anchor.c" }, + include_dirs = { "include" }, + targets = { ["mysql_connector_cpp"] = { kind = "lib" } }, + deps = { + ["compat.libmysqlclient"] = "8.4.6", + ["compat.openssl"] = "3.5.1", + }, + + linux = { + ldflags = { + "-Llib", "-l:libmysqlcppconnx-static.a", + "-l:libmysqlcppconn-static.a", "-lresolv", + }, + }, + macosx = { + ldflags = { + "-Llib", "-lmysqlcppconnx-static", + "-lmysqlcppconn-static", "-lresolv", + }, + }, + }, +} + +import("xim.libxpkg.pkginfo") +import("xim.libxpkg.log") + +local function sh_quote(value) + return "'" .. tostring(value):gsub("'", "'\\''") .. "'" +end + +local function resolve_tool(dep, name, fallback) + local pkg = pkginfo.build_dep(dep) + if pkg and pkg.bin then + local candidate = path.join(pkg.bin, name) + if os.isfile(candidate) then return candidate end + end + return fallback +end + +local function tail_lines(file, count) + local ok, content = pcall(io.readfile, file) + if not ok or not content then return "" end + local lines = {} + for line in (tostring(content) .. "\n"):gmatch("(.-)\n") do + lines[#lines + 1] = line + end + return table.concat(lines, "\n", math.max(1, #lines - count + 1), #lines) +end + +local function run(step, logf, command) + -- xlings 的 os.exec 通过返回值报告非零退出;pcall 只负责捕获 Lua 错误。 + local invoked, result, reason, code = + pcall(os.exec, string.format("bash -c %s", sh_quote(command))) + if invoked and result then return true end + local err = invoked and (tostring(reason) .. " " .. tostring(code)) + or tostring(result) + log.error("%s", "compat.mysql-connector-cpp: " .. step .. " failed (" + .. tostring(err) .. ")\n--- last 40 lines of " .. logf .. " ---\n" + .. tail_lines(logf, 40)) + return false +end + +local function find_source_root() + local ifile = pkginfo.install_file() + local roots = {} + if ifile then roots[#roots + 1] = tostring(ifile):replace(".tar.gz", "") end + roots[#roots + 1] = "mysql-connector-cpp-" .. pkginfo.version() + for _, root in ipairs(roots) do + if os.isfile(path.join(root, "CMakeLists.txt")) then return root end + end +end + +local function find_mysql_source_root(prefix) + local direct = path.join(prefix, "libmysqlclient-8.4.6") + if os.isfile(path.join(direct, "mcpp.toml")) then return direct end + for _, manifest in ipairs(os.files(path.join(prefix, "**", "mcpp.toml")) or {}) do + local root = path.directory(manifest) + if os.isfile(path.join(root, "include", "mysql.h")) then return root end + end +end + +local function patch_internal_compression_helper(srcroot) + local source = path.join(srcroot, "common", "session.cc") + local content = io.readfile(source) + local replacement = + "\nstatic TCPIP_options::compression_algorithm_t get_compression_algorithm(std::string alg)" + if content:find(replacement, 1, true) then return true end + + local signature = + "\nTCPIP_options::compression_algorithm_t get_compression_algorithm%(std::string alg%)" + local patched, count = content:gsub(signature, replacement) + if count ~= 1 then + log.error("compat.mysql-connector-cpp: expected one compression helper, found " + .. tostring(count)) + return false + end + io.writefile(source, patched) + return true +end + +function install() + local srcroot = find_source_root() + local mysql = pkginfo.install_dir("compat:libmysqlclient", "8.4.6") + local openssl = pkginfo.install_dir("compat:openssl", "3.5.1") + if not srcroot or not mysql or not openssl then + log.error("compat.mysql-connector-cpp: source/dependency prefix not found") + return false + end + + local mysql_src = find_mysql_source_root(mysql) + if not mysql_src then + log.error("compat.mysql-connector-cpp: libmysqlclient Form A source root not found") + return false + end + + local prefix = pkginfo.install_dir() + local builddir = path.join(srcroot, "mcpp-build") + local logf = path.join(prefix, "mcpp_mysql_connector_cpp_build.log") + os.tryrm(prefix) + os.mkdir(prefix) + os.tryrm(builddir) + + -- 此 helper 只在 session.cc 内使用;内部链接避免与 MySQL 8.4 + -- libmysqlclient 中同签名的实现发生静态链接符号冲突。 + if not patch_internal_compression_helper(srcroot) then return false end + + -- Connector/C++ 的配置阶段只要求能找到一个 mysqlclient archive;真正的 + -- libmysqlclient.a 会在最终消费时由 mcpp 的依赖图链接。这里的探测库不安装。 + local mysql_probe = path.join(builddir, "mysqlclient-probe") + os.mkdir(path.join(mysql_probe, "lib")) + os.mkdir(path.join(mysql_probe, "include")) + local probe_src = path.join(mysql_probe, "probe.c") + local probe_obj = path.join(mysql_probe, "probe.o") + local probe_lib = path.join(mysql_probe, "lib", "libmysqlclient.a") + io.writefile(probe_src, "int mcpp_mysqlclient_probe(void) { return 0; }\n") + local probe_command = string.format( + "cp -R %s/. %s/ && cp %s/*.h %s/ && " + .. "cc -c %s -o %s && ar rcs %s %s", + sh_quote(path.join(mysql_src, "include")), + sh_quote(path.join(mysql_probe, "include")), + sh_quote(path.join(mysql_src, "generated")), + sh_quote(path.join(mysql_probe, "include")), + sh_quote(probe_src), sh_quote(probe_obj), sh_quote(probe_lib), sh_quote(probe_obj)) + if not run("mysqlclient probe archive", logf, + probe_command .. " >" .. sh_quote(logf) .. " 2>&1") then + return false + end + + local cmake = resolve_tool("xim:cmake", "cmake", "cmake") + local make = resolve_tool("xim:make", "make", "make") + local jobs = (os.default_njob and os.default_njob()) or 4 + local clean_env = "env -u CPPFLAGS -u CFLAGS -u CXXFLAGS -u LDFLAGS " + local compiler = "" + if os.host() == "macosx" then + -- Connector 在 project() 前启动 bootstrap CMake;必须通过环境变量 + -- 将最低系统版本同步给 bootstrap 及其后续的内置依赖构建。 + clean_env = clean_env .. "MACOSX_DEPLOYMENT_TARGET=14.0 " + compiler = "-DCMAKE_C_COMPILER=/usr/bin/cc -DCMAKE_CXX_COMPILER=/usr/bin/c++ " + .. "-DCMAKE_OSX_DEPLOYMENT_TARGET=14.0 " + end + + local configure = string.format( + "%s%s -S %s -B %s -G \"Unix Makefiles\" " + .. "-DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=%s " + .. "-DCMAKE_INSTALL_LIBDIR=lib -DCMAKE_MAKE_PROGRAM=%s " + .. "-DBUILD_STATIC=ON -DWITH_JDBC=ON -DWITH_TESTS=OFF " + .. "-DWITH_DOC=OFF -DWITH_HEADER_CHECKS=OFF " + .. "-DOPENSSL_USE_STATIC_LIBS=TRUE -DWITH_SSL=%s " + .. "-DMYSQL_INCLUDE_DIR=%s/include -DMYSQL_LIB_DIR=%s/lib " + .. "-DMYSQLCLIENT_STATIC_LINKING=ON -DMYSQLCLIENT_STATIC_BINDING=ON %s", + clean_env, sh_quote(cmake), sh_quote(srcroot), sh_quote(builddir), + sh_quote(prefix), sh_quote(make), sh_quote(openssl), + sh_quote(mysql_probe), sh_quote(mysql_probe), compiler) + if not run("CMake configure", logf, configure .. " >" .. sh_quote(logf) .. " 2>&1") then + return false + end + + local build = string.format( + "%s%s --build %s --target connector -- -j%d >>%s 2>&1", + clean_env, sh_quote(cmake), sh_quote(builddir), jobs, sh_quote(logf)) + if not run("X DevAPI build", logf, build) then return false end + + local jdbc_build = string.format( + "%s%s --build %s --target connector-jdbc -- -j%d >>%s 2>&1", + clean_env, sh_quote(cmake), sh_quote(builddir), jobs, sh_quote(logf)) + if not run("JDBC build", logf, jdbc_build) then return false end + + local install_cmd = string.format( + "%s%s --install %s --component XDevAPIDev >>%s 2>&1", + clean_env, sh_quote(cmake), sh_quote(builddir), sh_quote(logf)) + if not run("X DevAPI install", logf, install_cmd) then return false end + + local jdbc_install = string.format( + "%s%s --install %s --component JDBCDev >>%s 2>&1", + clean_env, sh_quote(cmake), sh_quote(builddir), sh_quote(logf)) + if not run("JDBC install", logf, jdbc_install) then return false end + + if not os.isfile(path.join(prefix, "include", "mysqlx", "xdevapi.h")) + or not os.isfile(path.join(prefix, "include", "mysql", "jdbc.h")) + or not os.isfile(path.join(prefix, "lib", "libmysqlcppconnx-static.a")) + or not os.isfile(path.join(prefix, "lib", "libmysqlcppconn-static.a")) then + log.error("compat.mysql-connector-cpp: incomplete X DevAPI/JDBC install") + return false + end + + io.writefile(path.join(prefix, "mcpp_mysql_connector_cpp_anchor.c"), + "int mcpp_compat_mysql_connector_cpp_anchor(void) { return 0; }\n") + return true +end diff --git a/tests/examples/libmysqlclient/mcpp.toml b/tests/examples/libmysqlclient/mcpp.toml new file mode 100644 index 0000000..f473d3e --- /dev/null +++ b/tests/examples/libmysqlclient/mcpp.toml @@ -0,0 +1,16 @@ +[package] +name = "libmysqlclient-tests" +version = "0.1.0" + +# libmysqlclient 的 mcpp 源码构建目前只验证 Linux/macOS。 +[target.'cfg(linux)'.dependencies.compat] +libmysqlclient = "8.4.6" + +[target.'cfg(linux)'.build] +cxxflags = ["-DHAVE_LIBMYSQLCLIENT=1"] + +[target.'cfg(macos)'.dependencies.compat] +libmysqlclient = "8.4.6" + +[target.'cfg(macos)'.build] +cxxflags = ["-DHAVE_LIBMYSQLCLIENT=1"] diff --git a/tests/examples/libmysqlclient/tests/client.cpp b/tests/examples/libmysqlclient/tests/client.cpp new file mode 100644 index 0000000..67d212d --- /dev/null +++ b/tests/examples/libmysqlclient/tests/client.cpp @@ -0,0 +1,15 @@ +#ifdef HAVE_LIBMYSQLCLIENT +#include + +int main() { + static_assert(MYSQL_VERSION_ID == 80406); + if (mysql_get_client_version() != 80406) return 1; + + MYSQL* client = mysql_init(nullptr); + if (client == nullptr) return 2; + mysql_close(client); + return 0; +} +#else +int main() { return 0; } +#endif diff --git a/tests/examples/mysql-connector-cpp/mcpp.toml b/tests/examples/mysql-connector-cpp/mcpp.toml new file mode 100644 index 0000000..503250c --- /dev/null +++ b/tests/examples/mysql-connector-cpp/mcpp.toml @@ -0,0 +1,17 @@ +[package] +name = "mysql-connector-cpp-tests" +version = "0.1.0" + +# Connector/C++ 同时构建 X DevAPI 和 JDBC,并依赖源码构建的 +# compat.libmysqlclient/compat.openssl,因此目前只覆盖 Linux/macOS。 +[target.'cfg(linux)'.dependencies.compat] +mysql-connector-cpp = "26.7.0" + +[target.'cfg(linux)'.build] +cxxflags = ["-DHAVE_MYSQL_CONNECTOR_CPP=1"] + +[target.'cfg(macos)'.dependencies.compat] +mysql-connector-cpp = "26.7.0" + +[target.'cfg(macos)'.build] +cxxflags = ["-DHAVE_MYSQL_CONNECTOR_CPP=1"] diff --git a/tests/examples/mysql-connector-cpp/tests/connector.cpp b/tests/examples/mysql-connector-cpp/tests/connector.cpp new file mode 100644 index 0000000..2a6915b --- /dev/null +++ b/tests/examples/mysql-connector-cpp/tests/connector.cpp @@ -0,0 +1,20 @@ +#ifdef HAVE_MYSQL_CONNECTOR_CPP +#include +#include + +#include + +int main() { + // 同一个 Connector/C++ 包应同时提供 X DevAPI 和 JDBC。 + mysqlx::Value value(std::string("mcpp")); + if (value.get() != "mcpp") return 1; + + sql::mysql::MySQL_Driver* driver = sql::mysql::get_mysql_driver_instance(); + if (driver == nullptr) return 2; + if (driver->getMajorVersion() != 26) return 3; + if (driver->getMinorVersion() != 7) return 4; + return driver->getPatchVersion() == 0 ? 0 : 5; +} +#else +int main() { return 0; } +#endif From e281779deeae9404fd5ab6f1a840e4b5fb4b371e Mon Sep 17 00:00:00 2001 From: wellwei <96378453+wellwei@users.noreply.github.com> Date: Sat, 8 Aug 2026 22:39:54 +0800 Subject: [PATCH 2/9] fix(pkg): refresh compat.libmysqlclient sha after config repair The vendored libmysqlclient staging repo now defines HAVE_SYS_RESOURCE_H (plus the other POSIX macros upstream configure.cmake pairs with them) and carries Windows support sources; tag 8.4.6 was force-moved to the repaired commit, so the linux/macosx archives changed. mcpp-index still declares only linux+macosx for this package. --- pkgs/c/compat.libmysqlclient.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/c/compat.libmysqlclient.lua b/pkgs/c/compat.libmysqlclient.lua index 65da1f2..bce99d1 100644 --- a/pkgs/c/compat.libmysqlclient.lua +++ b/pkgs/c/compat.libmysqlclient.lua @@ -12,13 +12,13 @@ package = { linux = { ["8.4.6"] = { url = "https://github.com/wellwei/libmysqlclient/archive/refs/tags/8.4.6.tar.gz", - sha256 = "de2b7c42072e83005cff62c2506959865d923b31599f75a9d33521be99cf9e8d", + sha256 = "ceff60c7b41d9f1e75708e6173f403557211788d1aadcc54dce9a39105269fa4", }, }, macosx = { ["8.4.6"] = { url = "https://github.com/wellwei/libmysqlclient/archive/refs/tags/8.4.6.tar.gz", - sha256 = "de2b7c42072e83005cff62c2506959865d923b31599f75a9d33521be99cf9e8d", + sha256 = "ceff60c7b41d9f1e75708e6173f403557211788d1aadcc54dce9a39105269fa4", }, }, }, From 243115dae537a9ac38cb0f3576fc150086cc9b54 Mon Sep 17 00:00:00 2001 From: wellwei <96378453+wellwei@users.noreply.github.com> Date: Sat, 8 Aug 2026 22:53:11 +0800 Subject: [PATCH 3/9] fix(pkg): bump compat.libmysqlclient sha (strlcat/strlcpy on Linux) Linux toolchain glibc >= 2.38 declares strlcat/strlcpy; vendored config now defines HAVE_STRLCAT/HAVE_STRLCPY there so crypt_genhash_impl.cc uses libc instead of its static fallback. Tag 8.4.6 updated again. --- pkgs/c/compat.libmysqlclient.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/c/compat.libmysqlclient.lua b/pkgs/c/compat.libmysqlclient.lua index bce99d1..22411ea 100644 --- a/pkgs/c/compat.libmysqlclient.lua +++ b/pkgs/c/compat.libmysqlclient.lua @@ -12,13 +12,13 @@ package = { linux = { ["8.4.6"] = { url = "https://github.com/wellwei/libmysqlclient/archive/refs/tags/8.4.6.tar.gz", - sha256 = "ceff60c7b41d9f1e75708e6173f403557211788d1aadcc54dce9a39105269fa4", + sha256 = "27decd6716591d84a6f765a3d63fcd24031373841c684369445b83a840b38bea", }, }, macosx = { ["8.4.6"] = { url = "https://github.com/wellwei/libmysqlclient/archive/refs/tags/8.4.6.tar.gz", - sha256 = "ceff60c7b41d9f1e75708e6173f403557211788d1aadcc54dce9a39105269fa4", + sha256 = "27decd6716591d84a6f765a3d63fcd24031373841c684369445b83a840b38bea", }, }, }, From 9c687bd03815835adc9521d9a94d0ba2515dc80b Mon Sep 17 00:00:00 2001 From: wellwei <96378453+wellwei@users.noreply.github.com> Date: Sat, 8 Aug 2026 23:08:24 +0800 Subject: [PATCH 4/9] diag(connector): surface install-hook failures to Actions stdout The connector install hook fails with only an empty E_INTERNAL on macOS CI with no build log. Add stdout diagnostics (pcall wrapper, step values, run() error dump) to locate the failing step. Temporary diagnostic commit. --- pkgs/c/compat.mysql-connector-cpp.lua | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/pkgs/c/compat.mysql-connector-cpp.lua b/pkgs/c/compat.mysql-connector-cpp.lua index d6e2c05..6b47741 100644 --- a/pkgs/c/compat.mysql-connector-cpp.lua +++ b/pkgs/c/compat.mysql-connector-cpp.lua @@ -84,9 +84,11 @@ local function run(step, logf, command) if invoked and result then return true end local err = invoked and (tostring(reason) .. " " .. tostring(code)) or tostring(result) - log.error("%s", "compat.mysql-connector-cpp: " .. step .. " failed (" + local msg = "compat.mysql-connector-cpp: " .. step .. " failed (" .. tostring(err) .. ")\n--- last 40 lines of " .. logf .. " ---\n" - .. tail_lines(logf, 40)) + .. tail_lines(logf, 40) + log.error("%s", msg) + print(msg) -- [diag] surface to Actions stdout return false end @@ -129,15 +131,25 @@ local function patch_internal_compression_helper(srcroot) end function install() + -- [diag] diagnostics to stdout; strip before finalizing + print("compat.mysql-connector-cpp: install() start pkg_name=" .. tostring(pkginfo.name()) + .. " version=" .. tostring(pkginfo.version()) + .. " install_file=" .. tostring(pkginfo.install_file()) + .. " install_dir=" .. tostring(pkginfo.install_dir()) + .. " deps=" .. tostring(pkginfo.deps_list())) + local ok, result = pcall(function() local srcroot = find_source_root() local mysql = pkginfo.install_dir("compat:libmysqlclient", "8.4.6") local openssl = pkginfo.install_dir("compat:openssl", "3.5.1") + print("compat.mysql-connector-cpp: [diag] srcroot=" .. tostring(srcroot) + .. " mysql=" .. tostring(mysql) .. " openssl=" .. tostring(openssl)) if not srcroot or not mysql or not openssl then log.error("compat.mysql-connector-cpp: source/dependency prefix not found") return false end local mysql_src = find_mysql_source_root(mysql) + print("compat.mysql-connector-cpp: [diag] mysql_src=" .. tostring(mysql_src)) if not mysql_src then log.error("compat.mysql-connector-cpp: libmysqlclient Form A source root not found") return false @@ -146,6 +158,8 @@ function install() local prefix = pkginfo.install_dir() local builddir = path.join(srcroot, "mcpp-build") local logf = path.join(prefix, "mcpp_mysql_connector_cpp_build.log") + print("compat.mysql-connector-cpp: [diag] prefix=" .. tostring(prefix) + .. " builddir=" .. tostring(builddir) .. " logf=" .. tostring(logf)) os.tryrm(prefix) os.mkdir(prefix) os.tryrm(builddir) @@ -235,5 +249,11 @@ function install() io.writefile(path.join(prefix, "mcpp_mysql_connector_cpp_anchor.c"), "int mcpp_compat_mysql_connector_cpp_anchor(void) { return 0; }\n") - return true + end) + if not ok then + print("compat.mysql-connector-cpp: UNCAUGHT Lua error: " .. tostring(result)) + return false + end + print("compat.mysql-connector-cpp: install() result=" .. tostring(result)) + return result end From a53eaa261cc012eb85a226e514e349593552fc8e Mon Sep 17 00:00:00 2001 From: wellwei <96378453+wellwei@users.noreply.github.com> Date: Sat, 8 Aug 2026 23:14:31 +0800 Subject: [PATCH 5/9] diag(connector): write hook trace to a findable build log xlings swallows install-hook stdout/log.error, so the previous stdout diagnostics never reached the Actions log. Write every step (including a descriptor-load marker) to $HOME/.mcpp/registry/data/mcpp_mysql_connector_cpp_build.log, which validate.yml's 'find ... -name mcpp_*_build.log' dump tail-prints. --- pkgs/c/compat.mysql-connector-cpp.lua | 37 +++++++++++++++++---------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/pkgs/c/compat.mysql-connector-cpp.lua b/pkgs/c/compat.mysql-connector-cpp.lua index 6b47741..6734d3f 100644 --- a/pkgs/c/compat.mysql-connector-cpp.lua +++ b/pkgs/c/compat.mysql-connector-cpp.lua @@ -53,6 +53,19 @@ package = { import("xim.libxpkg.pkginfo") import("xim.libxpkg.log") +-- [diag] file-based diagnostics: written under $HOME/.mcpp/registry so the +-- validate.yml `find ... -name 'mcpp_*_build.log'` dump surfaces it even when +-- the hook's stdout/stderr are swallowed by xlings. Strip before finalizing. +local diagf = path.join(os.getenv("HOME") or "/tmp", ".mcpp/registry/data/mcpp_mysql_connector_cpp_build.log") +local function diag(msg) + local f = io.open(diagf, "a") + if f then + f:write(os.date("%H:%M:%S") .. " " .. tostring(msg) .. "\n") + f:close() + end +end +diag("descriptor loaded; cwd=" .. tostring(os.getcwd and os.getcwd())) + local function sh_quote(value) return "'" .. tostring(value):gsub("'", "'\\''") .. "'" @@ -88,7 +101,6 @@ local function run(step, logf, command) .. tostring(err) .. ")\n--- last 40 lines of " .. logf .. " ---\n" .. tail_lines(logf, 40) log.error("%s", msg) - print(msg) -- [diag] surface to Actions stdout return false end @@ -131,25 +143,22 @@ local function patch_internal_compression_helper(srcroot) end function install() - -- [diag] diagnostics to stdout; strip before finalizing - print("compat.mysql-connector-cpp: install() start pkg_name=" .. tostring(pkginfo.name()) - .. " version=" .. tostring(pkginfo.version()) - .. " install_file=" .. tostring(pkginfo.install_file()) - .. " install_dir=" .. tostring(pkginfo.install_dir()) - .. " deps=" .. tostring(pkginfo.deps_list())) + diag("install() called; install_file=" .. tostring(pkginfo.install_file()) + .. " install_dir=" .. tostring(pkginfo.install_dir()) + .. " deps=" .. tostring(pkginfo.deps_list())) local ok, result = pcall(function() local srcroot = find_source_root() local mysql = pkginfo.install_dir("compat:libmysqlclient", "8.4.6") local openssl = pkginfo.install_dir("compat:openssl", "3.5.1") - print("compat.mysql-connector-cpp: [diag] srcroot=" .. tostring(srcroot) - .. " mysql=" .. tostring(mysql) .. " openssl=" .. tostring(openssl)) + diag("step1 srcroot=" .. tostring(srcroot) .. " mysql=" .. tostring(mysql) + .. " openssl=" .. tostring(openssl)) if not srcroot or not mysql or not openssl then log.error("compat.mysql-connector-cpp: source/dependency prefix not found") return false end local mysql_src = find_mysql_source_root(mysql) - print("compat.mysql-connector-cpp: [diag] mysql_src=" .. tostring(mysql_src)) + diag("step2 mysql_src=" .. tostring(mysql_src)) if not mysql_src then log.error("compat.mysql-connector-cpp: libmysqlclient Form A source root not found") return false @@ -158,8 +167,8 @@ function install() local prefix = pkginfo.install_dir() local builddir = path.join(srcroot, "mcpp-build") local logf = path.join(prefix, "mcpp_mysql_connector_cpp_build.log") - print("compat.mysql-connector-cpp: [diag] prefix=" .. tostring(prefix) - .. " builddir=" .. tostring(builddir) .. " logf=" .. tostring(logf)) + diag("step3 prefix=" .. tostring(prefix) .. " builddir=" .. tostring(builddir) + .. " logf=" .. tostring(logf)) os.tryrm(prefix) os.mkdir(prefix) os.tryrm(builddir) @@ -251,9 +260,9 @@ function install() "int mcpp_compat_mysql_connector_cpp_anchor(void) { return 0; }\n") end) if not ok then - print("compat.mysql-connector-cpp: UNCAUGHT Lua error: " .. tostring(result)) + diag("UNCAUGHT Lua error: " .. tostring(result)) return false end - print("compat.mysql-connector-cpp: install() result=" .. tostring(result)) + diag("install() result=" .. tostring(result)) return result end From 52042b8f09adf17652d57ecc9f8727d839f609f9 Mon Sep 17 00:00:00 2001 From: wellwei <96378453+wellwei@users.noreply.github.com> Date: Sat, 8 Aug 2026 23:23:11 +0800 Subject: [PATCH 6/9] diag(connector): guard diag helper for descriptor-executing lint check_mirror_urls/check_package_name execute the descriptor with only import stubbed; top-level path.join crashed lint. Make diag lazy and a no-op unless the xlings runtime's path global is present. --- pkgs/c/compat.mysql-connector-cpp.lua | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/pkgs/c/compat.mysql-connector-cpp.lua b/pkgs/c/compat.mysql-connector-cpp.lua index 6734d3f..18baf33 100644 --- a/pkgs/c/compat.mysql-connector-cpp.lua +++ b/pkgs/c/compat.mysql-connector-cpp.lua @@ -55,9 +55,16 @@ import("xim.libxpkg.pkginfo") import("xim.libxpkg.log") -- [diag] file-based diagnostics: written under $HOME/.mcpp/registry so the -- validate.yml `find ... -name 'mcpp_*_build.log'` dump surfaces it even when --- the hook's stdout/stderr are swallowed by xlings. Strip before finalizing. -local diagf = path.join(os.getenv("HOME") or "/tmp", ".mcpp/registry/data/mcpp_mysql_connector_cpp_build.log") +-- the hook's stdout/stderr are swallowed by xlings. No-op outside the xlings +-- runtime (plain `path` global missing), so descriptor-executing lint helpers +-- stay green. Strip before finalizing. +local diagf local function diag(msg) + if type(path) ~= "table" then return end + if not diagf then + local home = os.getenv and os.getenv("HOME") or "/tmp" + diagf = home .. "/.mcpp/registry/data/mcpp_mysql_connector_cpp_build.log" + end local f = io.open(diagf, "a") if f then f:write(os.date("%H:%M:%S") .. " " .. tostring(msg) .. "\n") From a82bd40f8f4d824b3c48e872b0f1c84ea69f40a6 Mon Sep 17 00:00:00 2001 From: wellwei <96378453+wellwei@users.noreply.github.com> Date: Sat, 8 Aug 2026 23:33:16 +0800 Subject: [PATCH 7/9] fix(connector): find deps in shared cache + surface hook errors Root cause (from CI diagnostics): pkginfo.install_dir('compat:openssl') returns nil when openssl was installed into the shared registry cache by the sibling libmysqlclient member instead of this member's local xpkgs, so the install hook bailed with only an empty E_INTERNAL. - declare compat:libmysqlclient/compat:openssl as xpm deps so the resolver records them for this package - add find_dep_install_dir() fallback scanning the known xpkgs roots (member-local + $MCPP_HOME/registry/data/xpkgs) - write the hook trace + failure reason to $HOME/.mcpp/registry/data/mcpp_mysql_connector_cpp_build.log, which the validate.yml find-dump tail-prints (log.error/stdout are swallowed) - macOS: sanitize PATH to /usr/bin:/bin:/usr/sbin:/sbin so a GNU libtool on PATH (conda/homebrew) cannot shadow Apple's libtool in the CMake merge --- pkgs/c/compat.mysql-connector-cpp.lua | 94 ++++++++++++++++++++------- 1 file changed, 69 insertions(+), 25 deletions(-) diff --git a/pkgs/c/compat.mysql-connector-cpp.lua b/pkgs/c/compat.mysql-connector-cpp.lua index 18baf33..1f2e2d6 100644 --- a/pkgs/c/compat.mysql-connector-cpp.lua +++ b/pkgs/c/compat.mysql-connector-cpp.lua @@ -10,14 +10,23 @@ package = { xpm = { linux = { - deps = { "xim:cmake@latest", "xim:make@latest" }, + deps = { + "compat:libmysqlclient@8.4.6", + "compat:openssl@3.5.1", + "xim:cmake@latest", + "xim:make@latest", + }, ["26.7.0"] = { url = "https://github.com/mysql/mysql-connector-cpp/archive/refs/tags/26.7.0.tar.gz", sha256 = "b2299862eefc33fd71c0aac68328305671805fc955e6bd2578ef205c10f98550", }, }, macosx = { - deps = { "xim:cmake@latest" }, + deps = { + "compat:libmysqlclient@8.4.6", + "compat:openssl@3.5.1", + "xim:cmake@latest", + }, ["26.7.0"] = { url = "https://github.com/mysql/mysql-connector-cpp/archive/refs/tags/26.7.0.tar.gz", sha256 = "b2299862eefc33fd71c0aac68328305671805fc955e6bd2578ef205c10f98550", @@ -53,26 +62,27 @@ package = { import("xim.libxpkg.pkginfo") import("xim.libxpkg.log") --- [diag] file-based diagnostics: written under $HOME/.mcpp/registry so the --- validate.yml `find ... -name 'mcpp_*_build.log'` dump surfaces it even when --- the hook's stdout/stderr are swallowed by xlings. No-op outside the xlings --- runtime (plain `path` global missing), so descriptor-executing lint helpers --- stay green. Strip before finalizing. -local diagf -local function diag(msg) +-- Hook diagnostics: xlings swallows install-hook stdout and log.error, so a +-- failure used to surface only as an empty E_INTERNAL with no build log. This +-- hook writes a step trace + the failing reason to +-- $HOME/.mcpp/registry/data/mcpp_mysql_connector_cpp_build.log, which +-- validate.yml's `find ... -name 'mcpp_*_build.log'` dump tail-prints. It is a +-- no-op outside the xlings runtime (the `path` global is absent there), so the +-- descriptor-executing lint helpers stay green. +local hook_logf +local function hook_log(msg) if type(path) ~= "table" then return end - if not diagf then + if not hook_logf then local home = os.getenv and os.getenv("HOME") or "/tmp" - diagf = home .. "/.mcpp/registry/data/mcpp_mysql_connector_cpp_build.log" + hook_logf = home .. "/.mcpp/registry/data/mcpp_mysql_connector_cpp_build.log" end - local f = io.open(diagf, "a") + local f = io.open(hook_logf, "a") if f then f:write(os.date("%H:%M:%S") .. " " .. tostring(msg) .. "\n") f:close() end end -diag("descriptor loaded; cwd=" .. tostring(os.getcwd and os.getcwd())) - +hook_log("descriptor loaded") local function sh_quote(value) return "'" .. tostring(value):gsub("'", "'\\''") .. "'" @@ -108,6 +118,7 @@ local function run(step, logf, command) .. tostring(err) .. ")\n--- last 40 lines of " .. logf .. " ---\n" .. tail_lines(logf, 40) log.error("%s", msg) + hook_log(msg) return false end @@ -130,6 +141,30 @@ local function find_mysql_source_root(prefix) end end +-- `pkginfo.install_dir` scans only the member-local xpkgs roots; a dependency +-- that was installed into the shared registry cache (e.g. compat.openssl built +-- by the sibling libmysqlclient member and reused here without a member-local +-- copy) is invisible to it and comes back nil. Fall back to the known xpkgs +-- roots before giving up. +local function find_dep_install_dir(dep_name, dep_version) + local dir = pkginfo.install_dir(dep_name, dep_version) + if dir then return dir end + local ns, bare = dep_name:match("^([^:]+):(.+)$") + if not ns then return nil end + local store = ns .. "-x-" .. bare + local pfx = pkginfo.install_dir() + local roots = {} + if pfx then roots[#roots + 1] = path.directory(path.directory(pfx)) end + local home = os.getenv and os.getenv("MCPP_HOME") or "" + if home == "" then home = ((os.getenv and os.getenv("HOME")) or "") .. "/.mcpp" end + roots[#roots + 1] = path.join(home, "registry/data/xpkgs") + for _, root in ipairs(roots) do + local cand = path.join(root, store, dep_version) + if os.isdir(cand) then return cand end + end + return nil +end + local function patch_internal_compression_helper(srcroot) local source = path.join(srcroot, "common", "session.cc") local content = io.readfile(source) @@ -150,22 +185,26 @@ local function patch_internal_compression_helper(srcroot) end function install() - diag("install() called; install_file=" .. tostring(pkginfo.install_file()) + hook_log("install() called; install_file=" .. tostring(pkginfo.install_file()) .. " install_dir=" .. tostring(pkginfo.install_dir()) .. " deps=" .. tostring(pkginfo.deps_list())) local ok, result = pcall(function() local srcroot = find_source_root() - local mysql = pkginfo.install_dir("compat:libmysqlclient", "8.4.6") - local openssl = pkginfo.install_dir("compat:openssl", "3.5.1") - diag("step1 srcroot=" .. tostring(srcroot) .. " mysql=" .. tostring(mysql) - .. " openssl=" .. tostring(openssl)) + local mysql = find_dep_install_dir("compat:libmysqlclient", "8.4.6") + local openssl = find_dep_install_dir("compat:openssl", "3.5.1") + hook_log("srcroot=" .. tostring(srcroot) .. " mysql=" .. tostring(mysql) + .. " openssl=" .. tostring(openssl)) if not srcroot or not mysql or not openssl then - log.error("compat.mysql-connector-cpp: source/dependency prefix not found") + local why = "source/dependency prefix not found (srcroot=" + .. tostring(srcroot) .. " mysql=" .. tostring(mysql) + .. " openssl=" .. tostring(openssl) .. ")" + log.error("compat.mysql-connector-cpp: %s", why) + hook_log(why) return false end local mysql_src = find_mysql_source_root(mysql) - diag("step2 mysql_src=" .. tostring(mysql_src)) + hook_log("mysql_src=" .. tostring(mysql_src)) if not mysql_src then log.error("compat.mysql-connector-cpp: libmysqlclient Form A source root not found") return false @@ -174,8 +213,8 @@ function install() local prefix = pkginfo.install_dir() local builddir = path.join(srcroot, "mcpp-build") local logf = path.join(prefix, "mcpp_mysql_connector_cpp_build.log") - diag("step3 prefix=" .. tostring(prefix) .. " builddir=" .. tostring(builddir) - .. " logf=" .. tostring(logf)) + hook_log("prefix=" .. tostring(prefix) .. " builddir=" .. tostring(builddir) + .. " logf=" .. tostring(logf)) os.tryrm(prefix) os.mkdir(prefix) os.tryrm(builddir) @@ -215,6 +254,11 @@ function install() -- Connector 在 project() 前启动 bootstrap CMake;必须通过环境变量 -- 将最低系统版本同步给 bootstrap 及其后续的内置依赖构建。 clean_env = clean_env .. "MACOSX_DEPLOYMENT_TARGET=14.0 " + -- Sanitize PATH: the connector's CMake merge step invokes Apple's + -- libtool, and a GNU libtool earlier on PATH (conda/homebrew) breaks + -- the archive merge. cmake/make come from xim by absolute path and + -- cc/c++ are pinned below, so system-only PATH is safe here. + clean_env = clean_env .. "PATH=/usr/bin:/bin:/usr/sbin:/sbin " compiler = "-DCMAKE_C_COMPILER=/usr/bin/cc -DCMAKE_CXX_COMPILER=/usr/bin/c++ " .. "-DCMAKE_OSX_DEPLOYMENT_TARGET=14.0 " end @@ -267,9 +311,9 @@ function install() "int mcpp_compat_mysql_connector_cpp_anchor(void) { return 0; }\n") end) if not ok then - diag("UNCAUGHT Lua error: " .. tostring(result)) + hook_log("UNCAUGHT Lua error: " .. tostring(result)) return false end - diag("install() result=" .. tostring(result)) + hook_log("install() result=" .. tostring(result)) return result end From 5db43f3a6b04b44f838531bffd6755670a6c14bb Mon Sep 17 00:00:00 2001 From: wellwei <96378453+wellwei@users.noreply.github.com> Date: Sat, 8 Aug 2026 23:38:52 +0800 Subject: [PATCH 8/9] fix(connector): resolve xim cmake .app bundle, prepend system PATH CI reached CMake configure for the first time and failed with exit 127: resolve_tool fell back to bare 'cmake' because the xim cmake macOS payload is CMake.app/Contents/bin/cmake, not bin/cmake, and the previous fix replaced PATH entirely so the fallback had nothing to resolve. Handle the .app layout for an absolute path and prepend /usr/bin:/bin:/usr/sbin:/sbin to the existing PATH (system libtool wins, tools stay reachable). --- pkgs/c/compat.mysql-connector-cpp.lua | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/pkgs/c/compat.mysql-connector-cpp.lua b/pkgs/c/compat.mysql-connector-cpp.lua index 1f2e2d6..834af42 100644 --- a/pkgs/c/compat.mysql-connector-cpp.lua +++ b/pkgs/c/compat.mysql-connector-cpp.lua @@ -90,9 +90,17 @@ end local function resolve_tool(dep, name, fallback) local pkg = pkginfo.build_dep(dep) - if pkg and pkg.bin then - local candidate = path.join(pkg.bin, name) - if os.isfile(candidate) then return candidate end + if pkg then + local candidates = {} + if pkg.bin then candidates[#candidates + 1] = path.join(pkg.bin, name) end + if pkg.path then + -- macOS .app bundles (xim cmake ships CMake.app/Contents/bin). + candidates[#candidates + 1] = + path.join(pkg.path, name .. ".app", "Contents", "bin", name) + end + for _, cand in ipairs(candidates) do + if os.isfile(cand) then return cand end + end end return fallback end @@ -258,7 +266,8 @@ function install() -- libtool, and a GNU libtool earlier on PATH (conda/homebrew) breaks -- the archive merge. cmake/make come from xim by absolute path and -- cc/c++ are pinned below, so system-only PATH is safe here. - clean_env = clean_env .. "PATH=/usr/bin:/bin:/usr/sbin:/sbin " + clean_env = clean_env .. "PATH=/usr/bin:/bin:/usr/sbin:/sbin:" + .. (os.getenv("PATH") or "") .. " " compiler = "-DCMAKE_C_COMPILER=/usr/bin/cc -DCMAKE_CXX_COMPILER=/usr/bin/c++ " .. "-DCMAKE_OSX_DEPLOYMENT_TARGET=14.0 " end From 918b99340cc36488149e3d36004ed0e8d9fe3e13 Mon Sep 17 00:00:00 2001 From: wellwei <96378453+wellwei@users.noreply.github.com> Date: Sat, 8 Aug 2026 23:49:21 +0800 Subject: [PATCH 9/9] fix(connector): quote PATH value in env invocation A dev-machine PATH containing spaces (e.g. 'Library/Application Support/...') split the env command line and failed CMake configure with exit 127. Wrap the prepended PATH value in single quotes (sh_quote). --- pkgs/c/compat.mysql-connector-cpp.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/c/compat.mysql-connector-cpp.lua b/pkgs/c/compat.mysql-connector-cpp.lua index 834af42..b613bc4 100644 --- a/pkgs/c/compat.mysql-connector-cpp.lua +++ b/pkgs/c/compat.mysql-connector-cpp.lua @@ -266,8 +266,8 @@ function install() -- libtool, and a GNU libtool earlier on PATH (conda/homebrew) breaks -- the archive merge. cmake/make come from xim by absolute path and -- cc/c++ are pinned below, so system-only PATH is safe here. - clean_env = clean_env .. "PATH=/usr/bin:/bin:/usr/sbin:/sbin:" - .. (os.getenv("PATH") or "") .. " " + local system_path = "/usr/bin:/bin:/usr/sbin:/sbin:" .. (os.getenv("PATH") or "") + clean_env = clean_env .. "PATH=" .. sh_quote(system_path) .. " " compiler = "-DCMAKE_C_COMPILER=/usr/bin/cc -DCMAKE_CXX_COMPILER=/usr/bin/c++ " .. "-DCMAKE_OSX_DEPLOYMENT_TARGET=14.0 " end