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.