chewing / libchewing

libchewing - The intelligent phonetic input method library
https://chewing.im/
GNU Lesser General Public License v2.1
359 stars 90 forks source link

Linking fails with missing `chewing_version*` symbols on Arch Linux #602

Closed yan12125 closed 1 month ago

yan12125 commented 1 month ago

Describe the bug

On Arch Linux, a test fails to link due to missing symbols:

/usr/bin/ld: /tmp/cclNPtzO.ltrans0.ltrans.o: in function `test_runtime_version':
/usr/src/debug/libchewing-git/libchewing/tests/test-config.c:733:(.text+0x2ec): undefined reference to `chewing_version_major'
/usr/bin/ld: /usr/src/debug/libchewing-git/libchewing/tests/test-config.c:734:(.text+0x2f8): undefined reference to `chewing_version_minor'
/usr/bin/ld: /usr/src/debug/libchewing-git/libchewing/tests/test-config.c:735:(.text+0x301): undefined reference to `chewing_version_patch'
/usr/bin/ld: /usr/src/debug/libchewing-git/libchewing/tests/test-config.c:736:(.text+0x30a): undefined reference to `chewing_version_extra'
/usr/bin/ld: /usr/src/debug/libchewing-git/libchewing/tests/test-config.c:737:(.text+0x313): undefined reference to `chewing_version'
collect2: error: ld returned 1 exit status

Such errors happen after https://github.com/chewing/libchewing/pull/597

To Reproduce

  1. git clone https://aur.archlinux.org/libchewing-git.git
  2. cd libchewing-git && makepkg -s

(here I use the AUR package as it comes with a hack for ncurses - that's another issue I need to investigate and report)

Expected behavior Everything builds fine

Screenshots N/A

Platform (please complete the following information):

Additional context

Here, chewing_version* symbols are in a separate object file:

$ nm build/libchewing_capi.a
(...)
chewing_capi-d81d25450fb32c19.3svmc8inndydasnu.rcgu.o:
0000000000000000 T chewing_version
0000000000000000 T chewing_version_extra
0000000000000000 T chewing_version_major
0000000000000000 T chewing_version_minor
0000000000000000 T chewing_version_patch
0000000000000000 V __rustc_debug_gdb_scripts_section__
                 U _ZN4core3ffi5c_str4CStr6as_ptr17h4dfe4478f0616c52E
(...)

Probably that's why those symbols are dropped during linking.

I can force usage of the object file with:

diff --git a/CMakeLists.txt b/CMakeLists.txt
index ebf73c6..31032a7 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -193,6 +193,7 @@ if(BUILD_SHARED_LIBS)
             PRIVATE LINKER:-version-script,${PROJECT_SOURCE_DIR}/capi/src/symbols-elf.map
             PRIVATE LINKER:--gc-sections
             PRIVATE LINKER:-u,chewing_new
+            PRIVATE LINKER:-u,chewing_version
         )
         set_target_properties(libchewing PROPERTIES
             LINK_DEPENDS ${PROJECT_SOURCE_DIR}/capi/src/symbols-elf.map

But that may not be scalable with more and more symbols.

A more general fix might be --whole-archive. CMake >= 3.24 can use the WHOLE_ARCHIVE generator expression as indicated in https://stackoverflow.com/a/76761933. For example,

diff --git a/CMakeLists.txt b/CMakeLists.txt
index ebf73c6..e9707fd 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -185,7 +185,7 @@ target_include_directories(libchewing
 corrosion_set_env_vars(chewing_capi
     CHEWING_DATADIR=${CMAKE_INSTALL_FULL_DATADIR}/libchewing
 )
-target_link_libraries(libchewing PRIVATE chewing_capi)
+target_link_libraries(libchewing PRIVATE $<LINK_LIBRARY:WHOLE_ARCHIVE,$<TARGET_PROPERTY:chewing_capi,INTERFACE_LINK_LIBRARIES>>)
 target_link_libraries(chewing_capi INTERFACE ${SQLite3_LIBRARIES})
 if(BUILD_SHARED_LIBS)
     if(CMAKE_C_COMPILER_ID MATCHES GNU|^Clang)

Here INTERFACE_LINK_LIBRARIES is necessary, as the target chewing_capi created by corrosion is an inferface library: https://github.com/corrosion-rs/corrosion/blob/v0.5.0/cmake/Corrosion.cmake#L567

kanru commented 1 month ago

Ah, it is because I put the chewing_*version() methods in a separate file and so they are put in a separate object file.

yan12125 commented 1 month ago

Thanks for the fix! It's somewhat a surprise for me that WHOLE_ARCHIVE breaks linking with sqlite on Windows.