# Avoid GuessLibrarySOName on Windows

**URL:** https://discourse.cmake.org/t/avoid-guesslibrarysoname-on-windows/15158
**Category:** Development
**Tags:** os:windows
**Created:** [September 9, 2025, 7:55am UTC](https://discourse.cmake.org/t/avoid-guesslibrarysoname-on-windows/15158 "2025-09-09T07:55:27Z")
**Posts on this page:** 9
**Page:** 1

<div class="post-metadata">

### Author: ![stebjo](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/s/b487fb/32.png) [@stebjo](https://discourse.cmake.org/u/stebjo)
#### Post date: [September 9, 2025, 7:55am UTC](https://discourse.cmake.org/t/avoid-guesslibrarysoname-on-windows/15158/1 "2025-09-09T07:55:27Z")

</div>

**Hi,**

We’re adding external libraries using full paths in `target_link_libraries`. On Windows, this points to a `.lib` file. During link information processing, `.lib` files are treated as shared libraries, which triggers a call to `cmOrderDirectories::AddRuntimeLibrary`, and subsequently to `cmOrderDirectoriesConstraintSOName`.

This leads to `GuessLibrarySOName` instantiating `cmElf`, which opens the file to check if it’s an ELF library and then (obviously) always return false. However, file access on Windows is slow — and with corporate security tools, even slower. In one of our projects, the CMake generation phase takes around 150 seconds, and roughly 30% of that time (about 50 seconds) is spent on these unnecessary file reads. By short-circuiting `GuessLibrarySOName` to return `false` early, we can avoid opening and reading the file, significantly improving performance.

Our project includes many targets, external libraries, and four build configurations. We’ve tested this behavior with CMake 3.30 and 4.x — both exhibit the same issue. We use the “Visual Studio 17 2022” generator.

**Question:**  
Is there a recommended way to avoid these unnecessary file reads on Windows?  
It seems that both `.lib` and `.dll` are treated as shared libraries, which is why the ELF check is triggered.

---

<div class="post-metadata">

### Author: ![brad.king](https://discourse.cmake.org/user_avatar/discourse.cmake.org/brad.king/32/11_2.png) [@brad.king](https://discourse.cmake.org/u/brad.king)
#### Post date: [September 9, 2025, 1:13pm UTC](https://discourse.cmake.org/t/avoid-guesslibrarysoname-on-windows/15158/2 "2025-09-09T13:13:22Z")

</div>

Create imported libraries so CMake knows their type without guessing.

```cmake
add_library(foo STATIC IMPORTED)
set_property(TARGET foo PROPERTY IMPORTED_LOCATION "c:/path/to/foo.lib")
#...
target_link_libraries(myThing PRIVATE foo)

```

---

<div class="post-metadata">

### Author: ![stebjo](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/s/b487fb/32.png) [@stebjo](https://discourse.cmake.org/u/stebjo)
#### Post date: [September 9, 2025, 2:15pm UTC](https://discourse.cmake.org/t/avoid-guesslibrarysoname-on-windows/15158/3 "2025-09-09T14:15:24Z")

</div>

I experimented with using an interface-imported library alongside multiple statically imported libraries. On Linux, this setup introduces complexity due to the possibility of (true) shared libraries (`.so` files). To optimize, I switched from specifying full paths to using the `AddImportedLibrary`function below for some external packages that contain many library files. This change initially reduced generation time. However, when I applied it to more packages, the time increased again.

I didn’t investigate this further, assuming there must be a simpler way to avoid opening files on Windows just to determine whether they are ELF binaries. One thing I still find confusing is why `.lib` files on Windows are interpreted as shared libraries. Isn’t a `.lib` always linked the same way, regardless of whether it’s a static or import library?

Here’s the function I used:

```auto

function(AddImportedLibrary)
  set(options SHARED)
  set(oneValueArgs NAME)
  set(multiValueArgs DEBUG RELEASE INCLUDES)

  cmake_parse_arguments(ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})

  if(NOT ARGS_DEBUG)
    set(ARGS_DEBUG ${ARGS_RELEASE})
  else()
    list(LENGTH ARGS_DEBUG NBRDEB)
    list(LENGTH ARGS_RELEASE NBRREL)
    if(NOT (NBRDEB EQUAL NBRREL))
      message(FATAL_ERROR "DEBUG and RELEASE do not have the same length.")
    endif()
  endif()

  add_library(${ARGS_NAME} INTERFACE IMPORTED)
  target_include_directories(${ARGS_NAME} INTERFACE ${ARGS_INCLUDES})

  foreach(LIBD LIBR IN ZIP_LISTS ARGS_DEBUG ARGS_RELEASE)
    get_filename_component(FNAME "${LIBR}" NAME_WE)
    set(LIBNAME "${ARGS_NAME}::${FNAME}")
    if(ARGS_SHARED)
      add_library(${LIBNAME} SHARED IMPORTED)
    else()
      add_library(${LIBNAME} STATIC IMPORTED)
    endif()
    set_target_properties(${LIBNAME} PROPERTIES
      IMPORTED_LOCATION ${LIBR}
      IMPORTED_LOCATION_DEBUG ${LIBD})
    target_link_libraries(${ARGS_NAME} INTERFACE ${LIBNAME})
  endforeach()
endfunction()

```

---

<div class="post-metadata">

### Author: ![brad.king](https://discourse.cmake.org/user_avatar/discourse.cmake.org/brad.king/32/11_2.png) [@brad.king](https://discourse.cmake.org/u/brad.king)
#### Post date: [September 9, 2025, 2:26pm UTC](https://discourse.cmake.org/t/avoid-guesslibrarysoname-on-windows/15158/4 "2025-09-09T14:26:52Z")

</div>

Your `AddImportedLibrary` function is creating more than one imported target per library, which may explain increased generation time. A single imported target can represent both the library artifact(s) for all configurations and the usage requirements, except that we don’t model the possibility that a library is shared in one configuration and static in another.

> why `.lib` files on Windows are interpreted as shared libraries.

To what code are you referring?

---

<div class="post-metadata">

### Author: ![stebjo](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/s/b487fb/32.png) [@stebjo](https://discourse.cmake.org/u/stebjo)
#### Post date: [September 9, 2025, 3:04pm UTC](https://discourse.cmake.org/t/avoid-guesslibrarysoname-on-windows/15158/5 "2025-09-09T15:04:40Z")

</div>

The relevant call stack is:

```auto
cmSystemTools::GuessLibrarySOName
cmOrderDirectoriesConstraintSOName::cmOrderDirectoriesConstraintSOName
cmOrderDirectories::AddRuntimeLibrary
cmComputeLinkInformation::AddLibraryRuntimeInfo

```

In `AddLibraryRuntimeInfo`, the check for whether a file is a shared library is done via:

`is_shared_library = this->ExtractSharedLibraryName.find(file);`

This condition returns `true` if the file name ends with `.dll` or `.lib`.

### **Behavior of `AddImportedLibrary`**

The `AddImportedLibrary` function creates one imported interface target per external package. Each package may contain multiple library files (`.lib`, `.so` or `.a`), and for each of these, a separate target is added.

All these targets are either marked as `STATIC` or `SHARED` depending on the `SHARED` flag passed to the function. This behavior is consistent across configurations (Debug or not Debug) and does not vary per file.

---

<div class="post-metadata">

### Author: ![brad.king](https://discourse.cmake.org/user_avatar/discourse.cmake.org/brad.king/32/11_2.png) [@brad.king](https://discourse.cmake.org/u/brad.king)
#### Post date: [September 9, 2025, 5:55pm UTC](https://discourse.cmake.org/t/avoid-guesslibrarysoname-on-windows/15158/6 "2025-09-09T17:55:54Z")

</div>

This is a case of an abstraction leaking across platforms. `ExtractSharedLibraryName` gains`.lib` via `CMAKE_IMPORT_LIBRARY_SUFFIX` because that is the suffix for linking to a shared library on Windows, via its import library. The `is_shared_library` code path is probably not needed on Windows, so additional conditions/heuristics could be added to avoid it.

---

<div class="post-metadata">

### Author: ![stebjo](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/s/b487fb/32.png) [@stebjo](https://discourse.cmake.org/u/stebjo)
#### Post date: [September 10, 2025, 6:32am UTC](https://discourse.cmake.org/t/avoid-guesslibrarysoname-on-windows/15158/7 "2025-09-10T06:32:08Z")

</div>

Thanks for the clarification — this aligns perfectly with the issue I’ve been encountering.

Do you have any estimate on when a fix might be available? I’d really prefer to retain the simplicity of adding external dependencies via full paths, rather than switching to `add_library`. Unfortunately, the built-in `find_package` modules also rely on the less optimal approach of returning full paths in variables, which makes them unsuitable.

We’ve put a lot of effort into optimizing the configure step, but the generation phase on Windows continues to be a significant performance bottleneck. Removing unnecessary file reads is definitely a step in the right direction. That said, the real game changer on Windows would be the ability to parallelize the writing of project files, as file I/O is currently a major time sink.

---

<div class="post-metadata">

### Author: ![brad.king](https://discourse.cmake.org/user_avatar/discourse.cmake.org/brad.king/32/11_2.png) [@brad.king](https://discourse.cmake.org/u/brad.king)
#### Post date: [September 10, 2025, 2:59pm UTC](https://discourse.cmake.org/t/avoid-guesslibrarysoname-on-windows/15158/8 "2025-09-10T14:59:41Z")

</div>

I’ve opened [CMake Issue 27214](https://gitlab.kitware.com/cmake/cmake/-/issues/27214) for this.

---

<div class="post-metadata">

### Author: ![stebjo](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/s/b487fb/32.png) [@stebjo](https://discourse.cmake.org/u/stebjo)
#### Post date: [September 11, 2025, 1:36pm UTC](https://discourse.cmake.org/t/avoid-guesslibrarysoname-on-windows/15158/9 "2025-09-11T13:36:40Z")

</div>

Thank you. Given the tag on the ticket and the fact that the linked ticket addressing the same issue remains unresolved, it seems unlikely that a solution will be implemented in the near future.

Do you have any insight into the complexity of resolving this? My initial thought would be to first check whether the library is static using `ExtractStaticLibraryName` or perhaps against `CMAKE_IMPORT_LIBRARY_SUFFIX`, and then fall back to `ExtractSharedLibraryName`. This would change the precedence of the file extension variables, which could potentially introduce unintended side effects.

From what I can tell, only Windows-specific platform .cmake modules set `CMAKE_IMPORT_LIBRARY_SUFFIX`. So perhaps this suffix shouldn’t be added to `LinkShared` at all, but rather to `LinkStatic`?
