timosachsenberg opened a new issue, #51329:
URL: https://github.com/apache/arrow/issues/51329

   ### Describe the bug, including details regarding any error messages, 
version, and platform.
   
   ## Summary
   
   `libarrow_bundled_dependencies.a` contains `azure-storage-common`'s 
`xml_wrapper.cpp.o`, which references 23 libxml2 symbols. Arrow's exported 
CMake interface for the static targets names the bundle's other external 
requirements but not libxml2, so consumers who link `Arrow::arrow_static` are 
not told to link libxml2, and the link either fails outright or silently 
produces a library with unresolved `xml*` symbols.
   
   The same package's **shared** library declares the dependency correctly, 
which is what makes this look like an oversight in the static packaging rather 
than a deliberate choice.
   
   ## Version and platform
   
   - Arrow 24.0.0 (`libarrow-dev` / `libparquet-dev` 24.0.0-1) from the Apache 
APT repository
   - Ubuntu 24.04.4 x86_64, GCC 13.4.0, GNU ld 2.42 with the distribution 
default `--as-needed`
   - Still present on `main`: `cpp/src/arrow/CMakeLists.txt` has no 
`ARROW_AZURE` branch populating `ARROW_STATIC_INSTALL_INTERFACE_LIBS`, and 
`grep -i libxml cpp/cmake_modules/ThirdpartyToolchain.cmake` returns nothing.
   
   ## The 23 symbols, and the single object that needs them
   
   ```console
   $ cd /usr/lib/x86_64-linux-gnu
   $ nm -A --undefined-only libarrow_bundled_dependencies.a | grep -E " U 
xml[A-Z]" | head -3
   libarrow_bundled_dependencies.a:xml_wrapper.cpp.o:                 U 
xmlBufferCreate
   libarrow_bundled_dependencies.a:xml_wrapper.cpp.o:                 U 
xmlBufferFree
   libarrow_bundled_dependencies.a:xml_wrapper.cpp.o:                 U 
xmlCleanupParser
   ```
   
   All 23 are in that one object: `xmlBufferCreate`, `xmlBufferFree`, 
`xmlCleanupParser`, `xmlFreeTextReader`, `xmlFreeTextWriter`, `xmlInitParser`, 
`xmlNewTextWriterMemory`, `xmlReaderForMemory`, `xmlTextReaderConstName`, 
`xmlTextReaderConstValue`, `xmlTextReaderHasAttributes`, 
`xmlTextReaderHasValue`, `xmlTextReaderIsEmptyElement`, 
`xmlTextReaderMoveToNextAttribute`, `xmlTextReaderNodeType`, 
`xmlTextReaderRead`, `xmlTextWriterEndDocument`, `xmlTextWriterEndElement`, 
`xmlTextWriterStartDocument`, `xmlTextWriterStartElement`, 
`xmlTextWriterWriteAttribute`, `xmlTextWriterWriteElement`, 
`xmlTextWriterWriteString`.
   
   Its owner is the Azure SDK:
   
   ```console
   $ ar x libarrow_bundled_dependencies.a xml_wrapper.cpp.o
   $ nm -C --defined-only --extern-only xml_wrapper.cpp.o
   T Azure::Storage::_internal::XmlReader::XmlReader(char const*, unsigned long)
   T Azure::Storage::_internal::XmlReader::Read()
   T Azure::Storage::_internal::XmlWriter::GetDocument[abi:cxx11]()
   W Azure::Storage::_internal::XmlGlobalInitializer::~XmlGlobalInitializer()
   ...
   ```
   
   Not the AWS SDK, which is bundled too and contributes its own 
`xml_parser.c.o` — that one is aws-c-common's parser (`aws_xml_node_*`, 
`aws_xml_parse`) and has zero undefined `xml[A-Z]` symbols.
   
   ## Arrow's own code does not use libxml2
   
   Counting undefined `xml[A-Z]` symbols in the static archives shipped by 
`libarrow-dev` / `libparquet-dev`:
   
   ```
   libarrow.a                        0
   libparquet.a                      0
   libarrow_bundled_dependencies.a   23
   ```
   
   ## Why this looks like a packaging inconsistency
   
   The **shared** library from the very same package declares libxml2 itself:
   
   ```console
   $ nm -D --undefined-only libarrow.so.2400.0.0 | grep -cE "xml[A-Z]"
   23
   $ readelf -d libarrow.so.2400.0.0 | grep NEEDED | grep -i xml
    0x0000000000000001 (NEEDED)  Shared library: [libxml2.so.2]
   ```
   
   Same 23 symbols, dependency recorded. The static export, however, lists the 
bundle's other external requirements and omits only libxml2:
   
   ```cmake
   # /usr/lib/x86_64-linux-gnu/cmake/Arrow/ArrowTargets.cmake:80-84
   set_target_properties(Arrow::arrow_static PROPERTIES
     INTERFACE_LINK_LIBRARIES 
"Arrow::arrow_bundled_dependencies;OpenSSL::Crypto;OpenSSL::SSL;
       
Brotli::brotlienc;Brotli::brotlidec;Brotli::brotlicommon;BZip2::BZip2;LZ4::lz4;
       
Snappy::snappy;ZLIB::ZLIB;zstd::libzstd_shared;CURL::libcurl;utf8proc::utf8proc;
       re2::re2;protobuf::libprotobuf;Threads::Threads;dl;rt")
   ```
   
   `grep -ril libxml` over `cmake/Arrow*` and `cmake/Parquet*` returns nothing. 
The pkg-config side has the same gap — `arrow.pc`'s `Libs.private` is 
`-larrow_bundled_dependencies /usr/lib/x86_64-linux-gnu/libbz2.so -ldl` — which 
is the same family as #15139.
   
   ## The same file already establishes the pattern — for the AWS SDK
   
   On `main`, `cpp/src/arrow/CMakeLists.txt` populates 
`ARROW_STATIC_INSTALL_INTERFACE_LIBS` from fifteen `if(<dep>_SOURCE STREQUAL 
"SYSTEM")` branches and exactly **one** `BUNDLED` branch:
   
   ```cmake
     elseif(AWSSDK_SOURCE STREQUAL "BUNDLED")
       if(UNIX)
         list(APPEND ARROW_STATIC_INSTALL_INTERFACE_LIBS CURL::libcurl)
       endif()
     endif()
   ```
   
   That is precisely the case at hand: a vendored third party goes into 
`libarrow_bundled_dependencies.a`, so nothing about *it* needs declaring — but 
*its own external requirements* do, and are declared. The bundled AWS SDK gets 
that treatment for curl. The bundled Azure SDK never got it, for libxml2 or 
curl. There is no `ARROW_AZURE` block in that list at all, and `grep -i libxml 
cpp/cmake_modules/ThirdpartyToolchain.cmake` on `main` returns nothing, so 
libxml2 is never even resolved as a dependency.
   
   So this is not an open design question — it is a missing instance of a 
pattern that already exists one `elseif` away in the same file.
   
   ## How it surfaces for consumers
   
   In OpenMS we link `Arrow::arrow_static`. Because Arrow does not declare 
libxml2, we added it as a direct dependency of our own library — but CMake 
emits direct link libraries *before* the transitive archives that pull them in, 
so `libxml2.so` landed on the link line well ahead of 
`libarrow_bundled_dependencies.a`. With `--as-needed`, nothing had referenced 
libxml2 by that point, so the linker dropped it, and the archive then arrived 
with nothing left to resolve against. The result was a `libOpenMS.so` with 23 
unresolved `xml*` symbols that only failed at `dlopen` time:
   
   ```
   ImportError: .../libOpenMS.so: undefined symbol: xmlBufferFree
   ```
   
   A consumer cannot fix this correctly from the outside without reaching into 
Arrow's imported targets, because the ordering constraint is a property of 
Arrow's own interface: the workaround is to append `LibXml2::LibXml2` to 
`Arrow::arrow_bundled_dependencies`' `INTERFACE_LINK_LIBRARIES`, which is 
exactly the declaration that appears to be missing.
   
   ## Already visible in Arrow's own CI, and a maintainer asked for this issue
   
   #51280 (*"EXP: [C++][CI] Test static linking with S3"*, draft, open) added a 
static-link smoke test and hit this on ARM64 Ubuntu 22.04:
   
   > ```
   > /usr/bin/ld: libarrow_bundled_dependencies.a(xml_wrapper.cpp.o): in 
function
   > `Azure::Storage::_internal::XmlReader::XmlReader(char const*, unsigned 
long)':
   > .../azure-storage-common/src/xml_wrapper.cpp:416: undefined reference to 
`xmlReaderForMemory'
   > ```
   
   @raulcd's response there (2026-09-10) was:
   
   > In my opinion it is better if we tackle this separately on its own issue.
   
   I could not find that issue, so this is it.
   
   #51280's diff already sketches the fix:
   
   ```cmake
   # cpp/cmake_modules/ThirdpartyToolchain.cmake
   if(AZURE_SDK_VENDORED AND NOT WIN32)
     find_package(LibXml2 REQUIRED)
     list(APPEND ARROW_SYSTEM_DEPENDENCIES LibXml2)
   endif()
   
   # cpp/src/arrow/CMakeLists.txt
   if(ARROW_AZURE)
     if(Azure_SOURCE STREQUAL "SYSTEM")
       list(APPEND ARROW_STATIC_INSTALL_INTERFACE_LIBS 
${AZURE_SDK_LINK_LIBRARIES})
     elseif(Azure_SOURCE STREQUAL "BUNDLED")
       if(UNIX)
         list(APPEND ARROW_STATIC_INSTALL_INTERFACE_LIBS CURL::libcurl 
LibXml2::LibXml2)
       endif()
     endif()
   endif()
   ```
   
   Two notes on that sketch, in case it is lifted as-is:
   
   - It covers the CMake side only. `arrow.pc`'s `Requires.private` / 
`Libs.private` come from `ARROW_PC_REQUIRES_PRIVATE` / `ARROW_PC_LIBS_PRIVATE`, 
which the diff does not touch, so pkg-config consumers stay broken.
   - The same CI run shows the gap is not libxml2-specific. On ARM64 macOS 14 
the same static test fails on curl, pulled in by *two* bundled dependencies at 
once:
   
     > ```
     > "_curl_easy_cleanup", referenced from:
     >   Azure::Core::_internal::UniqueHandleDeleter<void, 
curl_easy_cleanup>::operator()(void*)
     >       in libarrow_bundled_dependencies.a[2](curl.cpp.o)
     >   google::cloud::rest_internal::v3_2::CurlPtrCleanup::operator()(void*) 
const
     >       in libarrow_bundled_dependencies.a[214](curl_handle.cc.o)
     > ```
   
     So a fix scoped to "Azure + libxml2" would leave the GCS/curl case open. 
Whether to widen this issue or split it is your call — libxml2 is simply the 
one that reached a released package and broke a downstream consumer.
   
   Related:
   - #50753 (open) — *"`ARROW_STATIC_INSTALL_INTERFACE_LIBS` declarations are 
usually forgotten because they are separate"*. Same list, and this report is 
arguably another data point for it, but that issue is an enhancement about the 
ergonomics of declaring **SYSTEM** dependencies (its driver was simdjson, 
#50739/#50741). libxml2 is not a SYSTEM dependency of Arrow — it is an external 
requirement of a **BUNDLED** third party, and Arrow never calls 
`resolve_dependency` for it, so co-locating declarations with 
`resolve_dependency` as proposed there would not surface this. Its only PR so 
far, #50764, was closed unmerged on 2026-08-03.
   
   Related but distinct:
   - #50776 (closed) — Meson could not *find* libxml2 in order to *build* Azure 
support; this issue is about the built artifact not *declaring* it to consumers.
   - #15139 (closed) — `arrow.pc` missing dependencies for Windows static 
builds; same family, pkg-config rather than CMake.
   
   ## Suggested fix
   
   Declare libxml2 alongside the other bundled-dependency requirements, so 
`Arrow::arrow_static` / `Arrow::arrow_bundled_dependencies` carry it in 
`INTERFACE_LINK_LIBRARIES` (and `arrow.pc` in `Libs.private`) whenever Arrow is 
built with Azure filesystem support and the Azure SDK is vendored into the 
bundle — mirroring what the shared build already records in `DT_NEEDED`. 
#51280's diff above is essentially that change.
   
   
   ### Component(s)
   
   C++
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to