make: no rule to make foo-NOTFOUND, means missing IMPORTED_IMPLIB

2021 March 20

An IMPLIB is an "import library", mainly used with DLLs on Windows. The C++ linker can link only to .lib files, thus SHARED libraries are really two files: the .dll that contains the actual code, and the .lib file that forwards the symbols to the .dll, i.e. tells the linker that the actual symbols are found in the corresponding .dll.

If you create a shared library, export it, generate fooTargets-*.cmake then CMake will correctly populate both IMPORTED_LIBRARY_<CONFIG> and IMPORTED_IMPLIB_<CONFIG> to the .dll and the .lib files, respectively.

However, if someone writes the export/import code manually, there may be errors in that. In the CMake ecosystem, this is not that rare, dozens (or hundreds?) of manually written Find modules are shipped with CMake in the system modules. Users also write their own, and distribute it with their libraries. This is a sadness on its own right, but let's ignore this for now.

What happens if one forgets to populate one of these properties? You'd expect a warning, an error, or some meaningful build error. This isn't the case.

There's another sadness related to this: IMPORTED libraries' CONFIG search order is obscure.

The search in the CONFIG priority order is implemented in a way that either the presence of LOCATION or the IMPLIB property will return a successful hit.

If e.g. IMPORTED_IMPLIB_RELEASE isn't present, but IMPORTED_LOCATION_RELEASE is present, then CMake will happily use the RELEASE properties. The search will stop at RELEASE with success, and RELEASE will be the active configuration for this library. Example:

set_target_properties(Hello::hellolib PROPERTIES
    IMPORTED_CONFIGURATIONS   "RELEASE;DEBUG"
    IMPORTED_LOCATION_RELEASE "${_IMPORT_PREFIX}/bin/hellolib.dll"
    IMPORTED_IMPLIB_RELEASE   "${_IMPORT_PREFIX}/lib/hellolib.lib"
    IMPORTED_LOCATION_DEBUG   "${_IMPORT_PREFIX}/bin/hellolib_d.dll"
    # NOTE: missing IMPORTED_IMPLIB_RELEASE
)

Regardless that CMake needs to use only the IMPLIB at link-time (which is missing), and has nothing to do with the LOCATION, which is a requirement only at run-time.

So it will try to link to a property value like foo-NOTFOUND, causing build-time error (!) without a single warning.

This gets terrible when you try to debug this. CMake provides debugging options only for configure time, and not for generate time. This is an issue during generation, because the e.g. Makefile generator will retrieve the IMPLIB property, which's value will be foo-NOTFOUND, and insert it into regular dependencies. You have to notice that IMPORTED_LOCATION_x is specified, but IMPORTED_IMPLIB_x is missing, there's no other way to find out. No signs of which property does it come from. Most likely, this bug is in a library's export code that you consume, which you are completely unfamiliar with. Especially if it's manually written Find module.

This gets even worse when you try to fix this. You can't unset a property, you can't make IMPORTED_LOCATION_x to be NOTFOUND too, to remove this from the search order. You can set it to empty string, but that's still present. Your best try is the same as for the search order: manipulate it manually by setting MAP_IMPORTED_CONFIG_<CONFIG>

Your best try is the same as for the search order: manipulate it manually by setting MAP_IMPORTED_CONFIG_<CONFIG>.

If you set CMAKE_MAP_IMPORTED_CONFIG_<CONFIG>, but you try to use a library that doesn't have a corresponding IMPORTED_LOCATION_<CONFIG>, you get the same error: build error that foo-NOTFOUND doesn't exist. In this case, set the MAP to something more permissive that includes the available configuration for foo library.