CMake:ExportInterface

From KitwarePublic
Revision as of 01:57, 16 January 2009 by Plowman (talk | contribs)
Jump to navigationJump to search

Export Interface for CMake Projects

This page documents a prototype design Alex and Brad created for exporting project information from a CMake project for use by outside projects.

The EXPORT command is used to export information from a project build tree:

EXPORT(TARGETS <target> ...
      FILE somefile.cmake
      [PREFIX someprefix-]
      [APPEND]
  )

Export files generated for the build tree always have the full paths in the LOCATION properties of the IMPORT targets it creates. The contents are written immediately to the specified file, if APPEND is used then it will be appended if the file already exists. This way it can be mixed with FILE(WRITE ...) and EXPORT_LIBRARY_DEPENDENCIES() commands (... which probably doesn't make a lot of sense).

The EXPORT mode of the INSTALL command is used to export information from an install tree:

INSTALL(
  EXPORT <export-name>
  DESTINATION lib/myproj-1.2
  FILE <file-for-install>.cmake
  [PREFIX someprefix-]
  )
INSTALL(
  TARGETS ...
  EXPORT <export-name> # attach these installed targest to the named export
  ...)

Export files generated for the install tree have LOCATION properties that may be relative to the installation prefix. The INSTALL(EXPORT ...) command creates code at the top of the export script that computes the install prefix. If the export script is installed relative to the installation prefix it computes the prefix relative to its own location. If the export script is installed to an absolute path the installation prefix is hard-coded into the file.

The INSTALL(TARGETS ... EXPORT ...) command creates code in the export script to specify the IMPORT target. If the destination is relative to the installation prefix the LOCATION property is specified relative to the install prefix set by the INSTALL(EXPORT ...) command. If the destination is to an absolute path that path is hard-coded into the LOCATION property.

Here is an example for exporting a simple project from the build tree and install tree:

EXPORT( # anonymous export must be fully specified here
  TARGETS mylibA mylibB mygen
  FILE myproj-build.cmake
  PREFIX myproj-1.2-
  )
INSTALL(
  TARGETS mylibA mylibB mygen
  EXPORT  myproj-install
  RUNTIME DESTINATION bin
  ARCHIVE DESTINATION lib
  LIBRARY DESTINATION lib
  )
INSTALL(
  EXPORT myproj-install
  DESTINATION lib/myproj-1.2
  FILE myproj-install.cmake
  PREFIX myproj-1.2-
  )

A more complicated project may be organized this way:

# CMakeLists.txt
SET(EXPORT_FILE myproj-build.cmake)
SET(EXPORT_PREFIX myproj-1.2- )

INSTALL(
  EXPORT myproj-install
  DESTINATION lib/myproj-1.2
  FILE myproj-install.cmake
  PREFIX ${EXPORT_PREFIX})
  )

# lib/CMakeLists.txt
EXPORT( TARGETS mylibA mylibB
        FILE ${EXPORTS_FILE}
        PREFIX ${EXPORTS_PREFIX}
  )
INSTALL(TARGETS mylibA mylibB
  EXPORT myproj-install
  RUNTIME DESTINATION bin
  ARCHIVE DESTINATION lib
  LIBRARY DESTINATION lib
  )

# exe/CMakeLists.txt
EXPORT(TARGETS mygen
       FILE ${EXPORTS_FILE}
       PREFIX ${EXPORTS_PREFIX}
  )
INSTALL(TARGETS mygen
  EXPORT myproj-install
  DESTINATION bin
  )

The generated export .cmake file for the build tree will have content such as

add_library(myproj-1.2-mylibA IMPORT)
set_target_properties(mylibA PROPERTIES
  LOCATION /path/to/libmylibA.a)

add_library(myproj-1.2-mylibB IMPORT)
set_target_properties(mylibB PROPERTIES
  LOCATION /path/to/libmylibB.a
  INTERFACE_LIBRARIES myproj-1.2-mylibA
  )

add_executable(myproj-1.2-mygen IMPORT)
set_target_properties(mylibB PROPERTIES
  LOCATION /path/to/mygen)

The generated export .cmake file for the install tree will have content such as

# PREFIX/lib/myproj-1.2/exports.cmake
get_filename_component(myproj-1.2-_PREFIX "${CMAKE_CURRENT_LIST_FILE}" PATH)
get_filename_component(myproj-1.2-_PREFIX "${myproj-1.2-_PREFIX}" PATH)
get_filename_component(myproj-1.2-_PREFIX "${myproj-1.2-_PREFIX}" PATH)
add_library(myproj-1.2-mylibA IMPORT)
set_target_properties(mylibA PROPERTIES
  LOCATION ${myproj-1.2-_PREFIX}/lib/libmylibA.a)

add_library(myproj-1.2-mylibB IMPORT)
set_target_properties(mylibB PROPERTIES
  LOCATION ${myproj-1.2-_PREFIX}/lib/libmylibB.a
  INTERFACE_LIBRARIES myproj-1.2-mylibA
  )

add_executable(myproj-1.2-mygen IMPORT)
set_target_properties(mylibB PROPERTIES
  LOCATION ${myproj-1.2-_PREFIX}/bin/mygen)


Alex, just writing down thoughts: In the install() commands above the COMPONENT feature is unused.

INSTALL(TARGETS mygen
  EXPORT myproj-install
  DESTINATION bin
  COMPONENT Runtime
  )

I think both COMPONENT and EXPORT are used to create groups of installed targets. Maybe the EXPORT keyword is not required but instead INSTALL(EXPORT ... ) could use the COMPONENTS ?

I think the typical use case for COMPONENT is to split packages into Runtime and Development. The Development component will include headers, static libraries, import libraries and cmake files. It may contain eventual code generators and tools, but they might also be in the Runtime component.

One problem is that the different parts of a shared library will typically be in different components. Maybe this could be ignored and the library could be exported if any of its parts (ARCHIVE, RUNTIME, LIBRARY) is in the given COMPONENT ? Let's say the ARCHIVE part of a library is in the Development component, and I export the Development component, then this library will also be exported.

But then this library will also be exported if the Runtime component is exported, since its RUNTIME part should belong to the Runtime component. OTOH, exporting a Runtime component doesn't make a lot of sense, because if you import something you are developing, so it must be development related stuff.

Then again, a code generator executable should belong to the Development component, but its DESTINATION will be specified in the RUNTIME part, while the destination of the dll part of a library will also be in the RUNTIME part but it should belong to the Runtime component. This may be slightly confusing if one INSTALL(TARGETS ...) command is used for libraries, executables and "development" executables.

So if such code generators belong to the Development component, and this component is exported, this will mean that also all the library targets will end up in the exported file, which will be good for the general case. When cross compiling it would probably be better if only the executables would go in the exported file, because the libraries aren't of any use then.

So a third component DevelopmentTools might be a good idea, which would contain just these executables. Then when creating a regular development package two components would have to be installed, Development and DevelopmentTools. I think this is currently not supported by the generated cmake_install.cmake scripts, don't know about cpack.

This could then also be added to the EXPORT() command, so that there is not only EXPORT(TARGETS ...), but also EXPORT(COMPONENT ...)