On 06/15/2011 12:56 AM, Lori Pritchett-Sheats wrote: > My project needs to create and install a Makefile that other client > projects could import to link against our project. T > > I have a simple template file that generates an exported Makefile using > the configure_file command, however I'm struggling with how to create a > variable that contains all the libraries our project builds and the > correct ordering for linking. I noticed in the CMakeCache.txt file there > are variables {target name}_LIB_DEPS with the dependencies listed, but > I can't find any documentation on these variables or find a way to > access all the library target names to even read these variables. > > I've looked at the Trilinos build system to see how they do this but > it's not straightforward. Does anyone know of a smaller project that > creates a exported Makefile that generates correct library link orders?
Look at the following exemplary project: # CMakeLists.txt: CMAKE_MINIMUM_REQUIRED(VERSION 2.8 FATAL_ERROR) PROJECT(MAKEFILE C) SET(CMAKE_VERBOSE_MAKEFILE ON) INSTALL(SCRIPT file.cmake) FILE(WRITE ${CMAKE_BINARY_DIR}/f.c "void f(void){}\n") FILE(WRITE ${CMAKE_BINARY_DIR}/g.c "void g(void){}\n") FILE(WRITE ${CMAKE_BINARY_DIR}/h.c "void h(void){}\n") ADD_LIBRARY(f SHARED f.c) ADD_LIBRARY(g SHARED g.c) ADD_LIBRARY(h SHARED h.c) TARGET_LINK_LIBRARIES(f g) TARGET_LINK_LIBRARIES(g h) ADD_CUSTOM_COMMAND(TARGET f COMMAND sh ${CMAKE_SOURCE_DIR}/register.sh $<TARGET_FILE:f>) ADD_CUSTOM_COMMAND(TARGET g COMMAND sh ${CMAKE_SOURCE_DIR}/register.sh $<TARGET_FILE:g>) ADD_CUSTOM_COMMAND(TARGET h COMMAND sh ${CMAKE_SOURCE_DIR}/register.sh $<TARGET_FILE:h>) INSTALL(TARGETS f DESTINATION /dev/shm/usr/bin) INSTALL(TARGETS g h DESTINATION lib) EXECUTE_PROCESS(COMMAND ${CMAKE_COMMAND} -E remove register.txt) EXECUTE_PROCESS(COMMAND ${CMAKE_COMMAND} -E touch register.txt) INSTALL(CODE "MESSAGE(\"Revert installs.txt to generate Makefile.\")") # register.sh: if ! grep "$1" register.txt; then echo "$1" >> register.txt fi # file.cmake: EXECUTE_PROCESS(COMMAND ${CMAKE_COMMAND} -E copy register.txt installs.txt) FUNCTION(FILE) IF(ARGV0 STREQUAL "INSTALL") MATH(EXPR N "${ARGC}-1") FOREACH(i RANGE 1 ${N}) IF(ARGV${i} STREQUAL "DESTINATION") MATH(EXPR j "${i}+1") SET(d "${ARGV${j}}") ENDIF() IF(ARGV${i} STREQUAL "FILES") MATH(EXPR j "${i}+1") SET(f "${ARGV${j}}") ENDIF() ENDFOREACH() _FILE(STRINGS installs.txt INSTALLS) _FILE(WRITE installs.txt) FOREACH(i IN LISTS INSTALLS) IF(i STREQUAL f) GET_FILENAME_COMPONENT(n "${i}" NAME) _FILE(APPEND installs.txt "${d}/${n}\n") ELSE() _FILE(APPEND installs.txt "${i}\n") ENDIF() ENDFOREACH() ENDIF() UNSET(ARGS) FOREACH(i IN LISTS ARGN) IF(i STREQUAL "") LIST(APPEND ARGS "\"\"") ELSE() LIST(APPEND ARGS "${i}") ENDIF() ENDFOREACH() _FILE(${ARGS}) ENDFUNCTION() The basic idea is as follows: The affected targets are equipped with a custom command which invokes the register.sh shell script with the target's location in the build tree as argument; these locations are collected in the register.txt file. As CMake builds the targets in the least-to-most dependent order, this order will be retained in register.txt. The register.sh script might also be written as a CMake script for increased platform independence. Now, to get a target's location after it's installed, one must tweak the cmake_install.cmake script since this is the only instance where both locations are explicitly denoted, AFAIK. The file.cmake script is called before the INSTALL(TARGETS ...) commands and redefines the FILE() command. In FILE(INSTALL ...), the new FILE() command version scans its parameters for the DESTINATION and FILES options, compares the latter's argument with the entries in register.txt and writes out the target's associated location in the installation directory to the installs.txt file. Of course, the original FILE() command is finally called; for this to work, empty parameters must be specified as \"\". In the end, the installs.txt file contains the installed targets in least-to-most dependent order. For the generation of a Makefile used by other projects, one should write a CMake script that is invoked by INSTALL(SCRIPT ...) after the INSTALL(TARGETS ...) commands, reads the install.txt file and uses the entries in reverted order. Subsequently, the newly generated Makefile can be installed via INSTALL(FILES ...). Alternatively, one might consider to scan the cmake_install.cmake script for "FILE(INSTALL ...)" patterns and pick up the DESTINATION and FILES options' arguments instead of redefining FILE(). Moreover, one should consider to remove installs.txt and the generated Makefile from the build tree via INSTALL(CODE|SCRIPT ...) as they'll belong to root after "su -c 'make install'", so otherwise, one could not delete the build tree anymore without the reacquisition of root privileges. '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