From d66db33740ade2dc4dc185bbce1599fa465f8aec Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Fri, 11 Sep 2026 19:25:31 +0800 Subject: [PATCH 1/2] fix(mysql-connector-cpp): match the consumer's standard library, or say why not MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CMake picks the system compiler here, so the static libs come out against libstdc++ whatever the consumer uses. On the llvm leg, which links libc++, the member fails at link with the libstdc++ half of its own dependency undefined: ld.lld: error: undefined symbol: std::_Rb_tree_increment(...) ld.lld: error: undefined symbol: std::__cxx11::basic_string<...>::_M_create(...) `llamacpp` refuses a libc++ toolchain by name with `mcpp::cxx_stdlib()`, and I said twice that the same lever was out of reach here because that is a build-program API and this is an inline descriptor. That was half right. The API is out of reach; the VARIABLE it reads is not necessarily — MCPP_CXX_STDLIB is exported when mcpp runs a build program (src/build/build_program.cppm), and whether it also reaches an xlings install hook is documented nowhere. So this asks instead of assuming, and records the answer either way: * visible and `libc++` — build with `-stdlib=libc++`, on the compile and both link flag sets. Compiling against libc++ headers while linking libstdc++ produces the same undefined symbols one layer later, so the flag has to reach all three. * absent — nothing changes. The build is what it was, and the hook log carries `MCPP_CXX_STDLIB=nil`, which is the missing half of the diagnosis the next time that leg fails. The failure has been read wrong once already in this area: `install() result=nil` was taken for a cause when it was a successful install with no return statement. A hook that reports what it saw is cheaper than a third round of inference. --- pkgs/c/compat.mysql-connector-cpp.lua | 35 +++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/pkgs/c/compat.mysql-connector-cpp.lua b/pkgs/c/compat.mysql-connector-cpp.lua index a65608ed..ede219ec 100644 --- a/pkgs/c/compat.mysql-connector-cpp.lua +++ b/pkgs/c/compat.mysql-connector-cpp.lua @@ -264,6 +264,41 @@ function install() 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 = "" + + -- THE STANDARD LIBRARY THIS IS BUILT AGAINST HAS TO MATCH THE CONSUMER'S. + -- + -- CMake picks the system compiler below, so the static libs come out + -- against libstdc++ whatever the consumer uses. On the llvm leg, which + -- links libc++, the member then fails at link with the libstdc++ half of + -- its own dependency undefined: + -- + -- ld.lld: error: undefined symbol: std::_Rb_tree_increment(...) + -- ld.lld: error: undefined symbol: + -- std::__cxx11::basic_string<...>::_M_create(...) + -- + -- `llamacpp` refuses a libc++ toolchain by name with + -- `mcpp::cxx_stdlib()`, but that is a build-program API and this is an + -- inline descriptor with no build program. What an install hook CAN do is + -- read the variable that API reads. mcpp exports MCPP_CXX_STDLIB when it + -- runs a build program (src/build/build_program.cppm); whether it reaches + -- an xlings install hook is not documented either way, so this asks + -- rather than assumes, and RECORDS the answer either way. + -- + -- Absent, nothing changes: the build is what it was, and the hook log says + -- the variable was not visible -- which is the missing half of the + -- diagnosis if the llvm leg fails again. + local want_stdlib = os.getenv("MCPP_CXX_STDLIB") + hook_log("MCPP_CXX_STDLIB=" .. tostring(want_stdlib)) + if want_stdlib == "libc++" and os.host() == "linux" then + -- -stdlib reaches the compile AND the link, which is what a mixed + -- build gets wrong: compiling against libc++ headers and linking + -- libstdc++ produces the same undefined symbols one layer later. + compiler = compiler + .. "-DCMAKE_CXX_FLAGS=-stdlib=libc++ " + .. "-DCMAKE_EXE_LINKER_FLAGS=-stdlib=libc++ " + .. "-DCMAKE_SHARED_LINKER_FLAGS=-stdlib=libc++ " + hook_log("building against libc++ to match the consumer") + end if os.host() == "macosx" then -- Connector 在 project() 前启动 bootstrap CMake;必须通过环境变量 -- 将最低系统版本同步给 bootstrap 及其后续的内置依赖构建。 From 6676e905d0a087d290e675f013e1e7a6c114547c Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Fri, 11 Sep 2026 20:13:13 +0800 Subject: [PATCH 2/2] =?UTF-8?q?fix(mysql-connector-cpp):=20the=20hook=20ca?= =?UTF-8?q?nnot=20know=20the=20consumer's=20stdlib=20=E2=80=94=20measured,?= =?UTF-8?q?=20then=20stated?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous commit on this branch asked a question: does MCPP_CXX_STDLIB reach an xlings install hook? It shipped a probe that logged the answer either way and applied `-stdlib=libc++` if the answer was yes. The answer is no, and this commit replaces the conditional with what was learned. Three independent levels agree: 1. MEASURED. `MCPP_CXX_STDLIB=nil` on `workspace (linux llvm 0/4)`, and the link failed exactly as before. 2. THE ONLY SETTER is `src/build/build_program.cppm` — mcpp exports it when it runs a BUILD PROGRAM. An install hook is not that. 3. NOTHING ELSE CROSSES either: `make_xlings_env` builds an `xlings::Env` of `{binary, home, projectDir}`, and `install_packages` is invoked with `XLINGS_HOME` and PATH. No toolchain, no compiler, no standard library. So neither of the two routes I had assumed were available actually is. `llamacpp` refuses a libc++ toolchain with `mcpp::cxx_stdlib()`, but that lives in its build program in its own repo; an inline descriptor has no such place. And `[target.'cfg(...)']` self-gating has a platform axis and no standard-library axis — a cfg selector is not a platform. The probe stays. One log line, it is the evidence for point 1, and the day mcpp passes the variable through it reports that and the fix becomes three lines directly below it. What the descriptor can do is stop being silent: it now states that these are static libraries built with the system compiler, that `std::__cxx11::` therefore crosses the boundary, and that a libc++ consumer cannot use them — where a user reads it, rather than where the linker says it. `validate.yml` keeps the member off the llvm leg, with the same reasoning written at the point that does the skipping. That is in CI and not in a descriptor for a reason worth saying out loud: the leg is the only layer that knows which standard library is in play. SKIPPED, NOT MARKED GREEN — the incompatibility is real for users on a libc++ toolchain; this stops CI re-measuring a known answer, it does not claim the combination works. The empty-shard message downstream said "No workspace member affected by this change", which would now be false in the case this change creates: `linux llvm 0/4` holds mysql-connector-cpp and nothing else, so skipping it empties the shard. It now covers both cases and points at the line that says which. Co-authored-by: sunrisepeak --- .github/workflows/validate.yml | 46 +++++++++++++++++++++- pkgs/c/compat.mysql-connector-cpp.lua | 56 ++++++++++++++++----------- 2 files changed, 79 insertions(+), 23 deletions(-) diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 4eaba2c9..645b53dd 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -1123,6 +1123,48 @@ jobs: shell: bash run: | mine='${{ fromJSON(needs.select.outputs.plan)[matrix.platform][format('{0}', matrix.shard)] }}' + + # ── A member its toolchain cannot build, and why that lives HERE ── + # + # Members self-gate with `[target.'cfg(...)']` (see the workspace + # comment above), and that has a platform axis and no STANDARD + # LIBRARY axis -- a cfg selector is not a platform. So a member that + # cannot be built under libc++ has nowhere of its own to say so, and + # the leg is the only layer that knows which standard library is in + # play. That is the whole reason this list is in CI and not in a + # descriptor; it is not a convenience. + # + # `mysql-connector-cpp`: the package builds STATIC libraries with + # CMake and the system compiler, so `std::__cxx11::` crosses the + # boundary and a libc++ consumer fails at link: + # + # ld.lld: error: undefined symbol: + # std::__cxx11::basic_string<...>::_M_create(...) + # + # Its install hook cannot adapt, because nothing tells it what the + # consumer chose: MCPP_CXX_STDLIB is exported only when mcpp runs a + # BUILD PROGRAM (`src/build/build_program.cppm`), `make_xlings_env` + # carries `{binary, home, projectDir}` and no toolchain, and a probe + # in the hook measured `MCPP_CXX_STDLIB=nil` on this very leg (#392). + # mcpp-community/mcpp#613; when the hook can see the consumer's stdlib, + # this entry comes out and the descriptor does the work instead. + # + # SKIPPED, NOT MARKED GREEN: the incompatibility is real for users on + # a libc++ toolchain. This stops CI from re-measuring a known answer + # every run; it does not claim the combination works. + if [ "${{ matrix.toolchain }}" = "llvm" ]; then + kept="" + for m in $mine; do + case "$m" in + mysql-connector-cpp) + echo "skipping '$m' on the llvm leg: static libs are built" \ + "against the system stdlib; see the comment above" ;; + *) kept="$kept $m" ;; + esac + done + mine="${kept# }" + fi + echo "MEMBERS=$mine" >> "$GITHUB_ENV" echo "shard ${{ matrix.shard }}/${{ matrix.shards }}: ${mine:-}" @@ -1191,7 +1233,9 @@ jobs: # optimise, and a local harness that differs from CI measures # something else. if [ -z "$MEMBERS" ]; then - echo "No workspace member affected by this change — nothing to test." + echo "No workspace member to test here: none affected by this" \ + "change, or every member of this shard was skipped on this" \ + "leg (the shard step above says which, and why)." else MCPP_TIMINGS="$PWD/timings.tsv" bash tests/run_members.sh $MEMBERS fi diff --git a/pkgs/c/compat.mysql-connector-cpp.lua b/pkgs/c/compat.mysql-connector-cpp.lua index ede219ec..90b598f5 100644 --- a/pkgs/c/compat.mysql-connector-cpp.lua +++ b/pkgs/c/compat.mysql-connector-cpp.lua @@ -276,29 +276,41 @@ function install() -- ld.lld: error: undefined symbol: -- std::__cxx11::basic_string<...>::_M_create(...) -- - -- `llamacpp` refuses a libc++ toolchain by name with - -- `mcpp::cxx_stdlib()`, but that is a build-program API and this is an - -- inline descriptor with no build program. What an install hook CAN do is - -- read the variable that API reads. mcpp exports MCPP_CXX_STDLIB when it - -- runs a build program (src/build/build_program.cppm); whether it reaches - -- an xlings install hook is not documented either way, so this asks - -- rather than assumes, and RECORDS the answer either way. + -- AND THIS HOOK CANNOT KNOW WHAT THE CONSUMER'S IS. That was asked as a + -- question and has now been answered; the answer is no, at three levels: -- - -- Absent, nothing changes: the build is what it was, and the hook log says - -- the variable was not visible -- which is the missing half of the - -- diagnosis if the llvm leg fails again. - local want_stdlib = os.getenv("MCPP_CXX_STDLIB") - hook_log("MCPP_CXX_STDLIB=" .. tostring(want_stdlib)) - if want_stdlib == "libc++" and os.host() == "linux" then - -- -stdlib reaches the compile AND the link, which is what a mixed - -- build gets wrong: compiling against libc++ headers and linking - -- libstdc++ produces the same undefined symbols one layer later. - compiler = compiler - .. "-DCMAKE_CXX_FLAGS=-stdlib=libc++ " - .. "-DCMAKE_EXE_LINKER_FLAGS=-stdlib=libc++ " - .. "-DCMAKE_SHARED_LINKER_FLAGS=-stdlib=libc++ " - hook_log("building against libc++ to match the consumer") - end + -- 1. MEASURED. The probe below logged `MCPP_CXX_STDLIB=nil` on the llvm + -- leg (mcpplibs/mcpp-index#392, `workspace (linux llvm 0/4)`), and + -- the link failed exactly as before. + -- 2. THE ONLY SETTER is `src/build/build_program.cppm` (`e.emplace_back + -- ("MCPP_CXX_STDLIB", env.cxxStdlib)`) -- mcpp exports it when it runs + -- a BUILD PROGRAM. An xlings install hook is not that. + -- 3. THE CALL CARRIES NOTHING ELSE either: `make_xlings_env` builds an + -- `xlings::Env` of `{binary, home, projectDir}`, and + -- `install_packages` is invoked with `XLINGS_HOME` and PATH. No + -- toolchain, no compiler, no stdlib crosses that boundary. + -- + -- So neither route is reachable from HERE. `llamacpp` refuses a libc++ + -- toolchain by name with `mcpp::cxx_stdlib()`, but that lives in its + -- build program, in its own repo; an inline descriptor has no such place, + -- and `[target.'cfg(...)']` self-gating has a platform axis and no + -- standard-library axis (a cfg selector is not a platform). + -- + -- WHAT IS TRUE ABOUT THIS PACKAGE, stated plainly so a user reads it + -- before the linker says it: these are STATIC libraries built by CMake + -- with the system compiler, so `std::__cxx11::` and friends cross the + -- boundary into the consumer. Consuming them from a libc++ toolchain does + -- not work and cannot be made to work from inside this hook. Tracked as + -- mcpp-community/mcpp#613 (install hooks need the consumer's stdlib); + -- until then + -- `validate.yml` keeps this member off the llvm leg, with the same reason + -- written there. + -- + -- The probe stays. It costs one log line, it is the evidence for point 1, + -- and the day mcpp does pass the variable through, this line reports it + -- and the fix becomes a three-line change directly below. + hook_log("MCPP_CXX_STDLIB=" .. tostring(os.getenv("MCPP_CXX_STDLIB")) + .. " (expected nil; see the comment above)") if os.host() == "macosx" then -- Connector 在 project() 前启动 bootstrap CMake;必须通过环境变量 -- 将最低系统版本同步给 bootstrap 及其后续的内置依赖构建。