Skip to content

[libclang][Cygwin] Provide unversioned DLL file alongside versioned one #147132

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

kikairoya
Copy link
Contributor

@kikairoya kikairoya commented Jul 5, 2025

On Cygwin, a shared library target generates a versioned DLL file like cyg${OUTPUT_NAME}-${VERSION}.dll and an import library lib${OUTPUT_NAME}.dll.a, but it does not generate the expected unversioned symlink cyg${OUTPUT_NAME}.dll as is typical on other Unix-like platforms.

However, dlopen() calls typically use the unversioned form (e.g. cygclang.dll), and this is consistent with usage on other platforms. In particular, clang-python relies on this behavior.

This patch creates a symbolic link named "cygclang.dll" pointing to the versioned DLL to improve compatibility with existing tooling and usage patterns.

On Cygwin, a shared library target generates a versioned DLL file like
"cyg${OUTPUT_NAME}-${VERSION}.dll" and an import library
"lib${OUTPUT_NAME}.dll.a", but it does *not* generate the expected
unversioned symlink "cyg${OUTPUT_NAME}.dll" as is typical on other Unix-like platforms.

However, `dlopen()` calls typically use the unversioned form (e.g. "cygclang.dll"),
and this is consistent with usage on other platforms. In particular, `clang-python`
relies on this behavior. Although the Cygwin runtime can resolve other forms like
"libclang.so" or "libclang.dll", a symlink alone won't suffice here, as `dlopen()`
ultimately calls `LoadLibraryExW`, which does not follow Cygwin-style symlinks.

Therefore, this patch installs an unversioned copy of the DLL without the version
suffix to improve compatibility with `dlopen()` and tools like `clang-python`.
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:as-a-library libclang and C++ API labels Jul 5, 2025
@llvmbot
Copy link
Member

llvmbot commented Jul 5, 2025

@llvm/pr-subscribers-clang

Author: Tomohiro Kashiwada (kikairoya)

Changes

On Cygwin, a shared library target generates a versioned DLL file like cyg${OUTPUT_NAME}-${VERSION}.dll and an import library lib${OUTPUT_NAME}.dll.a, but it does not generate the expected unversioned symlink cyg${OUTPUT_NAME}.dll as is typical on other Unix-like platforms.

However, dlopen() calls typically use the unversioned form (e.g. cygclang.dll), and this is consistent with usage on other platforms. In particular, clang-python relies on this behavior. Although the Cygwin runtime can resolve other forms like libclang.so or libclang.dll, a symlink alone won't suffice here, as dlopen() ultimately calls LoadLibraryExW, which does not follow Cygwin-style symlinks.

Therefore, this patch installs an unversioned copy of the DLL without the version suffix to improve compatibility with dlopen() and tools like clang-python.


Full diff: https://github.com/llvm/llvm-project/pull/147132.diff

1 Files Affected:

  • (modified) clang/tools/libclang/CMakeLists.txt (+18-5)
diff --git a/clang/tools/libclang/CMakeLists.txt b/clang/tools/libclang/CMakeLists.txt
index ac7a9a8db37c0..9c5ca26aab099 100644
--- a/clang/tools/libclang/CMakeLists.txt
+++ b/clang/tools/libclang/CMakeLists.txt
@@ -157,15 +157,28 @@ if(ENABLE_STATIC)
 endif()
 
 if(ENABLE_SHARED)
-  if(WIN32)
+  if(WIN32 OR CYGWIN)
     set_target_properties(libclang
       PROPERTIES
       VERSION ${LIBCLANG_LIBRARY_VERSION}
       DEFINE_SYMBOL _CINDEX_LIB_)
-      # Avoid declaring clang c++ symbols that are statically linked into libclang as dllimport'ed.
-      # If llvm/libclang-cpp dll is also being built for windows clang c++ symbols will still be
-      # implicitly be exported from libclang.
-      target_compile_definitions(libclang PRIVATE CLANG_BUILD_STATIC)
+    if (CYGWIN)
+      # On Cygwin environment, a library target generates "cyg${OUTPUT_NAME}-${VERSION}.dll" and "lib${OUTPUT_NAME}.dll.a" but
+      # don't provide link "cyg${OUTPUT_NAME}.dll" differs from expected as other Unix platforms.
+      # Although, to dlopen(), usually "cyg${OUTPUT_NAME}.dll" will be passed (or "lib${OUTPUT_NAME}.dll" and "lib${OUTPUT_NAME}.so"
+      # are also viable as Cygwin runtime replaces those prefix and suffix), which is same manner to other Unix platforms,
+      # and clang-python does so.
+      # Thus, put a copy of dll named without version suffix to convinience to use of dlopen(). A symbolic link can't be
+      # viable here as the path passed to dlopen() will be passed directly to LoadLibraryExW, so it must be a real file.
+      set(UNPAINTED_TARGET_NAME "$<TARGET_FILE_DIR:libclang>/$<TARGET_FILE_PREFIX:libclang>clang$<TARGET_FILE_SUFFIX:libclang>")
+      add_custom_command(TARGET libclang POST_BUILD
+                         COMMAND ${CMAKE_COMMAND} -E copy "$<TARGET_FILE:libclang>" "${UNPAINTED_TARGET_NAME}")
+      install(FILES "${UNPAINTED_TARGET_NAME}" DESTINATION "${CMAKE_INSTALL_BINDIR}" COMPONENT libclang)
+    endif()
+    # Avoid declaring clang c++ symbols that are statically linked into libclang as dllimport'ed.
+    # If llvm/libclang-cpp dll is also being built for windows clang c++ symbols will still be
+    # implicitly be exported from libclang.
+    target_compile_definitions(libclang PRIVATE CLANG_BUILD_STATIC)
   elseif(APPLE)
     set(LIBCLANG_LINK_FLAGS " -Wl,-compatibility_version -Wl,1")
     set(LIBCLANG_LINK_FLAGS "${LIBCLANG_LINK_FLAGS} -Wl,-current_version -Wl,${LLVM_VERSION_MAJOR}.${LLVM_VERSION_MINOR}.${LLVM_VERSION_PATCH}")

@jeremyd2019
Copy link
Contributor

a symlink alone won't suffice here, as dlopen() ultimately calls LoadLibraryExW, which does not follow Cygwin-style symlinks.

If dlopen doesn't work on a Cygwin symlink, that is a bug in dlopen and should be fixed in Cygwin.

@kikairoya
Copy link
Contributor Author

a symlink alone won't suffice here, as dlopen() ultimately calls LoadLibraryExW, which does not follow Cygwin-style symlinks.

If dlopen doesn't work on a Cygwin symlink, that is a bug in dlopen and should be fixed in Cygwin.

I had been misunderstood the functionality. That's cool. Thank you.

I made changes for description, commit and function body to use symlink.

@kikairoya
Copy link
Contributor Author

kikairoya commented Jul 6, 2025

I learned that a symlink works fine, so adding it to install-target might be a good idea. I'll look into that later.
I was idiot, I already wrote install(...).

@kikairoya
Copy link
Contributor Author

Updated for use relative path to link target.

@jeremyd2019
Copy link
Contributor

Perhaps the change in https://github.com/llvm/llvm-project/pull/136599/files#diff-f60dae8e82f67fa5e534e9ea566430d5b79f36119020865dc37b95d9a3635c5eR753 should be reconsidered instead, I wasn't thinking about dlopen in that case.

@kikairoya
Copy link
Contributor Author

kikairoya commented Jul 8, 2025

I'm thinking about each DLLs:

  • libLLVM:
    • On Linux:
      libLLVM.so(symlink) and libLLVM.so.21.0git are built and installed.
      libLLVM-21git.so(symlink) is installed but doesn't appear in build-tree.
    • On Cygwin:
      Only libLLVM-21git.dll.a and cygLLVM-21git.dll are built and installed.
      It might be better to make them symlinks and provide libLLVM.dll.a and cygLLVM-21.0git.dll as their link targets for consistency with other DLLs. These symlinks may be install-only targets.
      llvm-config should print -lLLVM-21git as ever for consistency with other Platforms.
      Additionally, installing a symlink cygLLVM.dll is convenient for dlopen.
  • libclang:
    • On Linux:
      libclang.so(symlink), libclang.so.21.0git(symlink) and libclang.so.21.0.0git are built and installed.
    • On Cygwin:
      Only libclang.dll.a and cygclang-21.0git.dll are built and installed.
      cygclang.dll(maybe a symlink) is needed for check-clang-python so it must be built in build-tree.
      This is the first motivation for this PR.
  • Other C libraries (e.g. Remarks):
    • On Linux:
      libRemarks.so(symlink) and libRemarks.so.21.0git are built and installed.
    • On Cygwin:
      libRemarks.dll.a and cygRemarks-21.0git.dll are built and installed.
      Additionally, installing a symlink cygRemarks.dll is convenient for dlopen.
  • Other DLLs without C interface (libclang-cpp):
    • On Linux:
      libclang-cpp.so(symlink) and libclang-cpp.so.21.0git are built and installed.
    • On Cygwin:
      libclang-cpp.dll.a and cygclang-cpp-21.0git.dll are built and installed.
      This DLL won't be used with dlopen since it have no C interface but installing symlink cygclang-cpp.dll as other DLLs isn't worse.

I had focused to make a symlink cygclang.dll but it could be applied to all other DLLs.
Also, I can make libLLVM-21git.dll.a a symlink to libLLVM.dll.a and make cygLLVM-21git.dll a symlink to cygLLVM-21.0git.dll.

So, installed-tree will be:

  • bin/
    • cygclang.dll@ -> cygclang-21.0git.dll
    • cygclang-21.0git.dll
    • cygclang-cpp.dll@ -> cygclang-cpp-21.0git.dll (may be omitted)
    • cygclang-cpp-21.0git.dll
    • cygLLVM.dll@ -> cygLLVM-21.0git.dll
    • cygLLVM-21git.dll@ -> cygLLVM-21.0git.dll (may not appear in build-tree)
    • cygLLVM-21.0git.dll
    • cygRemarks.dll@ -> cygRemarks-21.0git.dll
    • cygRemarks-21.0git.dll
  • lib/
    • libclang.dll.a
    • libclang-cpp.dll.a
    • libLLVM.dll.a
    • libLLVM-21git.dll.a@ -> libLLVM.dll.a (may not appear in build-tree)
    • libRemarks.dll.a

I would like to know if there are any concerns to do this.

@jeremyd2019
Copy link
Contributor

@mstorsjo ? I imagine whatever scheme is adopted should be considered with consistency with MinGW also.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:as-a-library libclang and C++ API clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants