On 01/21/2011 02:59 PM, Vincent Garcia wrote: > Dear CMake users, > > I have this BIG project which has tons of subdirectories (many levels). > In each (final) subdirectory, I create targets (executables and libraires). > So I have my top level CMakeLists.txt and one CMakeLists.txt file (which is > also a project) in each subdirectory. > Everything compiles just fine! > > My question is a general CMake question and is the following: > Imagine I can use ITK in my project. To do that, in the top level > CMakeLists.txt I define a variable USE_ITK. > If ITK is used, I search for it and I include useful files: > > OPTION(USE_ITK "If ON, search for Insight Toolkit package" OFF) > IF(USE_ITK) > FIND_PACKAGE(ITK) > IF(ITK_FOUND) > INCLUDE(${ITK_USE_FILE}) > ELSE(ITK_FOUND) > MESSAGE(FATAL_ERROR "ITK not found. Please set ITK_DIR.") > ENDIF(ITK_FOUND) > ELSE(USE_ITK) > SET(ITK_FOUND OFF) > ENDIF(USE_ITK) > > Now, if one subdirectory's project uses ITK, I have the following code (in > the subdir's CMakeLists.txt): > > FIND_PACKAGE( ITK ) > IF( NOT ITK_FOUND ) > MESSAGE( "Project ${PROJECT_NAME} requires ITK and ITK was not found. > ${PROJECT_NAME} will not be built." ) > RETURN() > ENDIF() > INCLUDE( ${ITK_USE_FILE} ) > > > As you can see, find_package(ITK) and INCLUDE( ${ITK_USE_FILE} ) are at least > called twice (in fact much more). > Is there any performance issue the method describe above?
Usually, find modules save their results in CMake's cache to avoid any repetitive searching for the same executables/libraries/files/... when invoked multiple times. E.g., the FIND_{PROGRAM,LIBRARY,...}() commands - which are used heavily in find modules - store their positive results in cached variables and don't perform their particular search again if these variables don't evaluate to -NOTFOUND. In general, well written find modules ensure that it is safe and cheap to call them more than once, so the performance penalty due to multiple invocations should be bearable, ideally even negligible. > Since it's not my code and since i'm a CMake newbee, I'd like to understand > if this is normal. > I would say that this should be done once in the top level CMakeLists.txt. > In the subdirs' CMakeLists, we should use only ITK_FOUND to decide if we > build the target or not. > Some people of my team agree and some don't. For several reasons, I'd join the latter group. ;) Please note that the following remarks are just my personal opinion, and of course, one can take up a different position. Basically, your approach tends to collect information about the needs of your subprojects/submodules in the top-level CMakeLists.txt which is in opposition to a good modularization and a tight locality. E.g., if one of your subprojects, situated deep in the overall project's directory tree, needs package XXX, the top-level CMakeLists.txt must know this fact to enable XXX by FIND_PACKAGE(XXX). As a result, your top-level CMakeLists.txt must know and enable all prerequisites of all subprojects which appears quite inconvenient to me, especially if your subprojects are somewhat selfcontained. When using multi-component packages, the situation might even become worse: Suppose you have two subprojects, app1 uses QtGui while app2 uses QtXml. The top-level CMakeLists.txt according to your approach would look like: # CMakeLists.txt: CMAKE_MINIMUM_REQUIRED(VERSION 2.8 FATAL_ERROR) PROJECT(APPS C CXX) FIND_PACKAGE(Qt4 COMPONENTS QtCore QtGui QtXml) INCLUDE(${QT_USE_FILE}) ADD_SUBDIRECTORY(app1) ADD_SUBDIRECTORY(app2) Now, look at the following app{1,2}/CMakeLists.txt: # app1/CMakeLists.txt: FILE(WRITE ${CMAKE_CURRENT_BINARY_DIR}/main.c "int main(void){return 0;}\n" ) ADD_EXECUTABLE(app1 main.c) TARGET_LINK_LIBRARIES(app1 ${QT_LIBRARIES}) # app2/CMakeLists.txt: FILE(WRITE ${CMAKE_CURRENT_BINARY_DIR}/main.c "int main(void){return 0;}\n" ) ADD_EXECUTABLE(app2 main.c) TARGET_LINK_LIBRARIES(app2 ${QT_LIBRARIES}) With this configuration, app{1,2} get linked against Qt{Core,Gui,Xml}, i.e. both will be overlinked. This is a consequence of the joint list of components in FIND_PACKAGE(Qt4 ...) for both subprojects; in other words: A consequence of information about those subprojects' needs, collected in the top-level CMakeLists.txt. > What is the best way to proceed? In each sufficiently selfcontained subproject, one should issue the FIND_PACKAGE() commands for all of the subprojects' prerequisites in order to minimize the dependency on any higher-level CMakeLists.txt. OTOH, if a subproject consists of several tightly connected modules, e.g., it's possibly not appropriate to do the same for each module. Instead, one might concentrate FIND_PACKAGE()'s invocations in the subproject's CMakeLists.txt and let the modules inherit the results as long as there are no difficulties like the one mentioned above. Probably, the question of concentrating or distributing FIND_PACKAGE() calls for prerequisites in extensive projects, or finding a reasonable trade-off, respectively, can't be answered in a general way. Rather, it depends on the degree of modularization, locality and independence you want to achieve for your subprojects/submodules as well as the latters' degree of interconnection. At least, I would not vote for the extreme approach of concentrating everything in the top-level CMakeLists.txt. There's another aspect of this topic when a package is needed in a CMakeLists.txt as well as in a subordinated one, especially with multi-component packages. E.g.: # CMakeLists.txt: CMAKE_MINIMUM_REQUIRED(VERSION 2.8 FATAL_ERROR) PROJECT(ACCUMULATION C CXX) FIND_PACKAGE(Qt4 COMPONENTS QtCore QtGui) INCLUDE(${QT_USE_FILE}) FILE(WRITE ${CMAKE_CURRENT_BINARY_DIR}/main.c "int main(void){return 0;}\n" ) ADD_EXECUTABLE(main main.c) TARGET_LINK_LIBRARIES(main ${QT_LIBRARIES}) ADD_SUBDIRECTORY(module) # module/CMakeLists.txt: FIND_PACKAGE(Qt4 COMPONENTS QtCore QtXml) INCLUDE(${QT_USE_FILE}) FILE(WRITE ${CMAKE_CURRENT_BINARY_DIR}/main.c "int main(void){return 0;}\n" ) ADD_EXECUTABLE(module main.c) TARGET_LINK_LIBRARIES(module ${QT_LIBRARIES}) Here, the module gets linked against QtGui although the requested components are QtCore and QtXml. This is because FindQt4.cmake acts in an accumulative manner, i.e. its results and effects incorporate those of a previous invocation within the same scope. IMO, this is problematic, and I'd prefer that the results and effects of a find module do depend only on the parameters passed to FIND_PACKAGE() - the list of components, in particular - well-known variables like CMAKE_PREFIX_PATH and explicitly documented variables like, e.g., XXX_ROOT_DIR. In the example above, one can prevent the results of the top-level CMakeLists.txt from propagating to the module's one by issuing the ADD_SUBDIRECTORY() before the FIND_PACKAGE(). So, when distributing FIND_PACKAGE() invocations among subprojects or modules, it's worth to look where they appear in the diverse CMakeLists.txt files w.r.t. ADD_SUBDIRECTORY() in order to control the downward propagation of their results/effects. BTW, introducing the USE_ITK option in the top-level CMakeLists.txt is a good method to enable/disable ITK in the subprojects. Although this means a certain coupling between the subprojects and the top- level CMakeLists.txt, too, it's a quite loose one; particularly the subprojects' configurations remain valid even if this option would not be defined anywhere. 'hope that helps. Regards, Michael _______________________________________________ Powered by www.kitware.com Visit other Kitware open-source projects at http://www.kitware.com/opensource/opensource.html Please keep messages on-topic and check the CMake FAQ at: http://www.cmake.org/Wiki/CMake_FAQ Follow this link to subscribe/unsubscribe: http://www.cmake.org/mailman/listinfo/cmake