[CMake] I'm confused with Find*.cmake and *-config.cmake

Michael Hertling mhertling at online.de
Wed Apr 21 21:37:56 EDT 2010


On 04/20/2010 09:40 PM, Benoit Thomas wrote:

> On 2010-04-20 03:27, Michael Hertling wrote:
>> On 04/19/2010 10:07 PM, Benoit Thomas wrote:
>>
>>    
>>> I'm using an external library which is already compiled. I have wrapped
>>> the library in a MyLib.cmake and use it in cmake as an imported library.
>>> It works fine, but since the include line uses relative path, it changes
>>> from projects to projects and in some case it looks just wrong.
>>>      
>> Just for my understanding: What exactly do you mean with "wrapped the
>> library in a MyLib.cmake" and "use it in cmake as an imported library"?
>>    
> I'm migrating Visual Studio project to CMake. The Visual Studio projects 
> use heavy configurations (20+). So, when I used a library which hasn't 
> yet been converted to cmake, I usually only create a small 
> CMakeLists.txt for it which look like this:
> 
> add_library (MyLib IMPORTED)
> set_target_properties (MyLib PROPERTIES IMPORTED_LOCATION_CONFIG1 
> "${MYLIB_ROOT_DIR}/bin/config1/MyLib.lib")
> set_target_properties (MyLib PROPERTIES IMPORTED_LOCATION_CONFIG2 
> "${MYLIB_ROOT_DIR}/bin/config2/MyLib.lib")
> ...
> set_target_properties (MyLib PROPERTIES IMPORTED_LOCATION_CONFIG20 
> "${MYLIB_ROOT_DIR}/bin/config20/MyLib.lib")
> 
> Then, in another project I'm doing (something like):
> 
> include (../../../somewhere/MyLib/CMakeLists.txt)
> 
> add_executable (MyExe)
> target_link_libraries (MyExe MyLib)
> 
> But this line didn't feel right:
> include (../../../somewhere/MyLib/CMakeLists.txt)
> 
> I renamed the CMakeLists.txt to MyLib.cmake as it's not really a project 
> file on his own. Also, the path is never  the same for all projects so 
> that's why I wanted to change the format to a find_package (it's 
> cleaner, and doesn't change if projects are moved).

Oh, I see: Using the IMPORT option of ADD_LIBRARY(). That's the point I
didn't catch; the term "wrapped the library in..." slightly confused me.

>>> I want to change this to something more like:
>>>
>>> find_package (MyLib)
>>> include (MYLIB_CMAKE_FILE)
>>> # add_executable and stuff
>>> target_link_libraries (MyExe MyLib)
>>>      
>> This is a possible approach which is also followed by FindQt4.cmake,
>> but, for several reasons, you shouldn't explicitly refer to "MyLib"
>> in TARGET_LINK_LIBRARIES(); use variables from FIND_PACKAGE(MyLib).
>>
>>    
> I though about using variables, but target_link_libraries doesn't 
> support configurations, and the other alternative I found was using 
> LINK_FLAGS. However, using imported library and target_link_libraries 
> gave me a clean solution.

For me, that's absolutely reasonable. As fas as I understand, the
flexible link time configuration is one of the primary motives for
deploying imported targets instead of the, say, classic method of
referring directly to <path/to/library>. For the use of variables in
TARGET_LINK_LIBRARIES() when it comes to import libraries, see below.

>>> In the book, there is an example which seems to do more what I want and
>>> use *-config.cmake, which give something like
>>>
>>> find_package (MyLib)
>>> # no include, since the MyLib-config.cmake already took care of the include
>>> # add_executable and stuff
>>> target_link_libraries (MyExe MyLib)
>>>      
>> What is meant with "took care of the include"? I don't have the book. ;)
>>    
> When I look at FindXXX.cmake, they create variables and macros, but they 
> don't add any targets to your project (I assume this was standard). In 
> the book, there's an example about creating XXX-config.cmake files. From 
> my understanding, the XXX-config.cmake include a XXX-targets.cmake which 
> create the imported target.

I suppose XXX-targets.cmake is a file created by INSTALL(EXPORT ...)
and, thus, contains IMPORTED definitions for all targets declared as
"EXPORT" in INSTALL(TARGETS ...). This file is thought to be included
by other project's CMakeLists.txt files in order to make the targets
available as imported, but here, one has the same question as you had
initially: Where does this "target file" resides? Though I can't cite
an example, it's reasonable for me that find modules and config files
include the target file; a find module searches it and a config file
knows it because, usually, they were installed together by the same
CMakeLists.txt. Provided that, a foreign CMakeLists.txt could invoke
FIND_PACKAGE(XXX) for indirect inclusion of the target file, making
XXX's libraries, executables etc. finally available as IMPORTED
targets. Perhaps, it's this you're interested in? However, see:

<http://www.cmake.org/Wiki/CMake_2.6_Notes#Exporting_Targets>

> So I was a little confuse about the fact that if I write code like
> 
> find_package (MyLib)
> 
> # If MyLib is a FindMyLib.cmake, the following code should like
> if (MyLib_FOUND)
>      include (MyLib_USE_FILE) # which would do the add_library (MyLib 
> IMPORTED)
> else()
>      # print some fatal error message...
> endif()
> 
> # But if MyLib is a MyLib-config.cmake, there's nothing else to do; 
> find_package will display an error message if MyLib isn't found, and if 
> it's found, MyLib-config.cmake will include MyLib-target.cmake

Since FindMyLib.cmake and mylib-config.cmake (lowercase with hyphen)
would be loaded - only one of them, of course - by one and the same
invocation of FIND_PACKAGE(MyLib) they should behave the same from a
user's point of view, i.e. either both include ${MyLib_USE_FILE} or
none. In any case, you would end up with your example above or with

FIND_PACKAGE(MyLib)
IF(NOT MyLib_FOUND)
     # Think how to proceed.
ENDIF()

if FindMyLib.cmake/mylib-config.cmake would actually execute the
include command. Here, the main difference between find module and
config file is that FindMyLib.cmake has to look for ${MyLib_USE_FILE}
while mylib-config.cmake "knows" since it is part of the MyLib package,
or, in other words, a find module is typically developed and installed
separately from the package it is up to find; see MW's earlier reply.

>>> Part of what I don't understand is when a user use the find_package
>>> command, is he expecting to load a Find* file or a *-config file (or he
>>> doesn't care?), [...]
>>>      
>> In general, he doesn't care; in particular, she can tell
>> FIND_PACKAGE() to look for a config file exclusively.
>>    
> The more I'm reading about the subject, the more I'll go with a 
> XXX-config.cmake file which will take care of importing the library. And 
> as you mention, I'll try to use variables as much as possible, but in my 
> context is a little hard :) I'll also use the NO_MODULE option to make 
> it clear I'm looking for a config file and not a module.

In regard to imported libraries in find modules / config files and
their backward communication via variables, you should take a look at
the current ${CMAKE_ROOT}/Modules/FindQt4.cmake. Up to now, this is the
only find module delivered with CMake which uses both imported libraries
and variables to communicate its results. Perhaps, it does quite exactly
what you have in mind w.r.t. your challenges. Additionally, see here:

<http://www.mail-archive.com/cmake@cmake.org/msg27109.html>
<http://www.mail-archive.com/cmake@cmake.org/msg26546.html>

And especially:

<http://www.mail-archive.com/cmake@cmake.org/msg15760.html>

Obviously, imported targets are having a bright future. ;)

>> As for myself: Yes, absolutely. FIND_PACKAGE(MyLib) should try to find
>> anything necessary to use Mylib, i.e. preprocessor definitions, include
>> directories, libraries etc., and communicate the results as variables,
>> e.g. MyLib_DEFINITIONS, MyLib_INCLUDE_DIRS, MyLib_LIBRARIES etc., but it
>> should not have a direct, hard-coded impact on the configuration process
>> as a target definition would be; this should be reserved for the calling
>> CMake script. BTW, why would you need a target "MyLib" here? As you said
>> before, the library is external and already compiled.
>>    
> The reason why I need a MyLib target is because of the 20+ 
> configurations... Make my life more easy that way (Keeping the 20+ 
> configurations in a single project is not really my call btw...)

Of course, this number of configurations is a compelling reason for the
use of imported libraries. My concern about adding them in find modules
or config files was that all targets - imported or, say, regular - must
be unique, so I have a certain objection when FIND_PACKAGE() returns
with new targets defined. In contrast, variables somehow feel more
gentle on that score.

Anyway, revisiting your previous question about targets brought in by
FIND_PACKAGE(), IMPORTED targets are different, indeed: FindQt4.cmake
defines them and the INSTALL(EXPORT ...) command supports them via the
target file, so I would conclude: It's fine to bring in *imported*
targets by FIND_PACKAGE(), but give a thought to namespaces; see
NAMESPACE option of INSTALL(EXPORT ...) and "Qt4::".

In summary, I would advise:

- For a cmakified or going-to-be-cmakified project, write a target file
  and a config file and install them at an appropriate location where
  the config file can be found by FIND_PACKAGE().
- By design, the config file does know the location of the target file
  and, thus, can include it without any objections, but consider the
  use of namespaces.
- The manually written target file should be set up in the same way
  INSTALL(EXPORT ...) would do it, so there's no regression if the
  project's cmakification is completed later.

Again, 'hope this helps a bit, but I'm not sure if it's the way things
are intended to be done. Perhaps, a CMake expert can provide us some
insight into the respective "best practice".

Best regards,

Michael Hertling



More information about the CMake mailing list