CMake 2.6 Notes: Difference between revisions

From KitwarePublic
Jump to navigationJump to search
Line 99: Line 99:
(in practice the <code>${PREFIX}</code> is computed relative to the file location automatically).
(in practice the <code>${PREFIX}</code> is computed relative to the file location automatically).
An outside project may now contain code such as
An outside project may now contain code such as
  include(${PREFIX}/lib/myproj/myproj-targets.cmake)          # 1
  set(GENERATED_SRC ${CMAKE_CURRENT_BINARY_DIR}/generated.c)
  add_custom_command(
    OUTPUT ${GENERATED_SRC}
    COMMAND generator ${GENERATED_SRC}                        # 2
    )
  add_executable(myexe src1.c src2.c ${GENERATED_SRC})
Line #1 loads the target import script.  The script may import any number of targets.  Their locations are computed relative to the script location so the install tree may be moved around.
The importing project need only find the import script (see the packaging section of this document for how this might be done).
Line #2 references the generator executable in a custom command.  The resulting build system will run the executable from its installed location.
Libraries may also be exported and imported:
  add_library(foo STATIC foo1.c)
  install(TARGETS foo DESTINATION lib EXPORTS myproj-targets)
  install(EXPORT myproj-targets DESTINATION lib/myproj)
This installs the library and an import file referencing it.  Outside projects may simply write


   include(${PREFIX}/lib/myproj/myproj-targets.cmake)
   include(${PREFIX}/lib/myproj/myproj-targets.cmake)
   add_custom_command(
   add_executable(myexe src1.c)
  target_link_libraries(myexe foo)
 
and the executable will be linked to the library <code>foo</code> exported and installed by the original project.
 
Any number of target installations may be associated with the same export name.
The export names are considered global so any directory may contribute a target installation.
Only one call to the <code>install(EXPORT)</code> command is needed to install an import file that references all targets.
Both of the examples above may be combined into a single export, even if they are in different subdirectories of the project:
 
  # A/CMakeLists.txt
  add_executable(generator generator.c)
  install(TARGETS generator DESTINATION lib/myproj/generators EXPORT myproj-targets)
 
  # B/CMakeLists.txt
  add_library(foo STATIC foo1.c)
  install(TARGETS foo DESTINATION lib EXPORTS myproj-targets)
 
  # Top CMakeLists.txt
  add_subdirectory(A)
  add_subdirectory(B)
  install(EXPORT myproj-targets DESTINATION lib/myproj)


=Preprocessor Definitions=
=Preprocessor Definitions=

Revision as of 22:47, 28 January 2008

This page documents some of the changes and new features available in CMake 2.6.

Exporting and Importing Targets

CMake 2.6 introduces support for exporting targets from one project and importing them into another. The main feature allowing this functionality is the notion of an IMPORTED target. Here we present imported targets and then show how CMake files may be generated by a project to export its targets for use by other projects.

Importing Targets

Imported targtes are used to convert files outside the project on disk into logical targets inside a CMake project. They are created using the IMPORTED option to add_executable and add_library commands. No build files are generated for imported targets. They are useful simply for convenient, flexible reference to outside executables and libraries.

Consider the following example which creates and uses an IMPORTED executable target:

 add_executable(generator IMPORTED)                                                     # 1
 set_property(TARGET generator PROPERTY IMPORTED_LOCATION "/path/to/some_generator")    # 2
 
 set(GENERATED_SRC ${CMAKE_CURRENT_BINARY_DIR}/generated.c)
 add_custom_command(
   OUTPUT ${GENERATED_SRC}
   COMMAND generator ${GENERATED_SRC}                                                   # 3
   )
 
 add_executable(myexe src1.c src2.c ${GENERATED_SRC})

Line #1 creates a new CMake target called "generator". Line #2 tells CMake the location of the file on disk to import. Line #3 references the target in a custom command. The generated build system will contain a command line such as

 /path/to/some_generator /project/binary/dir/generated.c

in the rule to generate the source file.

Libraries may also be used through imported targets:

 add_library(foo IMPORTED)
 set_property(TARGET foo PROPERTY IMPORTED_LOCATION /path/to/libfoo.a)
 add_executable(myexe src1.c src2.c)
 target_link_libraries(myexe foo)

The generated build system will contain a command line such as

 ... -o myexe /path/to/libfoo.a ...

in the rule to link myexe. On Windows a .dll and its .lib import library may be imported together:

 add_library(bar IMPORTED)
 set_property(TARGET bar PROPERTY IMPORTED_LOCATION c:/path/to/bar.dll)
 set_property(TARGET bar PROPERTY IMPORTED_IMPLIB c:/path/to/bar.lib)
 add_executable(myexe src1.c src2.c)
 target_link_libraries(myexe bar)

The generated build system will contain a command line such as

 ... -o myexe.exe c:/path/to/bar.lib ...

in the rule to link myexe. A library with multiple configurations may be imported with a single target:

 add_library(foo IMPORTED)
 set_property(TARGET foo PROPERTY IMPORTED_LOCATION_RELEASE c:/path/to/foo.lib)
 set_property(TARGET foo PROPERTY IMPORTED_LOCATION_DEBUG   c:/path/to/foo_d.lib)
 add_executable(myexe src1.c src2.c)
 target_link_libraries(myexe foo)

The generated build system will link myexe to foo.lib when it is built in the release configuration and foo_d.lib when built in the debug configuration.

Exporting Targets

Imported targets on their own are useful, but they still require the project that imports targets to know the locations of the target files on disk. The real power of this feature is when the project providing the target files also provides a file to help import them.

The install(TARGETS) and install(EXPORT) commands work together to install a target and a file to help import it. For example, the code

 add_executable(generator generator.c)
 install(TARGETS generator DESTINATION lib/myproj/generators EXPORT myproj-targets)
 install(EXPORT myproj-targets DESTINATION lib/myproj)

installs the files

 <prefix>/lib/myproj/generators/generator
 <prefix>/lib/myproj/myproj-targets.cmake

The myproj-targets.cmake file contains code such as

 add_executable(generator IMPORTED)
 set_property(
   TARGET generator
   PROPERTY IMPORTED_LOCATION ${PREFIX}/lib/myproj/generators/generator
   )

(in practice the ${PREFIX} is computed relative to the file location automatically). An outside project may now contain code such as

 include(${PREFIX}/lib/myproj/myproj-targets.cmake)          # 1
 set(GENERATED_SRC ${CMAKE_CURRENT_BINARY_DIR}/generated.c)
 add_custom_command(
   OUTPUT ${GENERATED_SRC}
   COMMAND generator ${GENERATED_SRC}                        # 2
   )
 add_executable(myexe src1.c src2.c ${GENERATED_SRC})

Line #1 loads the target import script. The script may import any number of targets. Their locations are computed relative to the script location so the install tree may be moved around. The importing project need only find the import script (see the packaging section of this document for how this might be done). Line #2 references the generator executable in a custom command. The resulting build system will run the executable from its installed location.

Libraries may also be exported and imported:

 add_library(foo STATIC foo1.c)
 install(TARGETS foo DESTINATION lib EXPORTS myproj-targets)
 install(EXPORT myproj-targets DESTINATION lib/myproj)

This installs the library and an import file referencing it. Outside projects may simply write

 include(${PREFIX}/lib/myproj/myproj-targets.cmake)
 add_executable(myexe src1.c)
 target_link_libraries(myexe foo)

and the executable will be linked to the library foo exported and installed by the original project.

Any number of target installations may be associated with the same export name. The export names are considered global so any directory may contribute a target installation. Only one call to the install(EXPORT) command is needed to install an import file that references all targets. Both of the examples above may be combined into a single export, even if they are in different subdirectories of the project:

 # A/CMakeLists.txt
 add_executable(generator generator.c)
 install(TARGETS generator DESTINATION lib/myproj/generators EXPORT myproj-targets)
 
 # B/CMakeLists.txt
 add_library(foo STATIC foo1.c)
 install(TARGETS foo DESTINATION lib EXPORTS myproj-targets)
 
 # Top CMakeLists.txt
 add_subdirectory(A)
 add_subdirectory(B)
 install(EXPORT myproj-targets DESTINATION lib/myproj)

Preprocessor Definitions

Preprocessor definitions may now be added to builds with much finer granularity than in previous versions of CMake. There is a new property called COMPILE_DEFINITIONS that is defined directories, targets, and source files. For example, the code

 add_library(mylib src1.c src2.c)
 add_executable(myexe main1.c)
 set_property(
   DIRECTORY
   PROPERTY COMPILE_DEFINITIONS A AV=1
   )
 set_property(
   TARGET mylib
   PROPERTY COMPILE_DEFINITIONS B BV=2
   )
 set_property(
   SOURCE src1.c
   PROPERTY COMPILE_DEFINITIONS C CV=3
   )

will build the source files with these definitions:

 src1.c:   -DA -DAV=1 -DB -DBV=2 -DC -DCV=3
 src2.c:   -DA -DAV=1 -DB -DBV=2
 main2.c:  -DA -DAV=1

When the add_definitions command is called with flags like "-DX" the definitions are extracted and added to the current directory's COMPILE_DEFINITIONS property. When a new subdirectory is created with add_subdirectory the current state of the directory-level property is used to initialize the same property in the subdirectory.

Note in the above example that the set_property command will actually set the property and replace any existing value. The command provides the APPEND option to help add more definitions without removing existing ones. For example, the code

 set_property(
   SOURCE src1.c
   APPEND PROPERTY COMPILE_DEFINITIONS D DV=4
   )

will add the definitions "-DD -DDV=4" when building src1.c.

Definitions may also be added on a per-configuration basis using the COMPILE_DEFINITIONS_<CONFIG> property. For example, the code

 set_property(
   TARGET mylib
   PROPERTY COMPILE_DEFINITIONS_DEBUG MYLIB_DEBUG_MODE
   )

will build sources in mylib with -DMYLIB_DEBUG_MODE only when compiling in a Debug configuration.

Link Line Generation

CMake 2.6 implements a new approach to generating link lines for targets.

Consider these libraries:

  /path/to/libfoo.a
  /path/to/libfoo.so

Previously if someone wrote

  target_link_libraries(myexe /path/to/libfoo.a)

CMake would generate this code to link it:

  ... -L/path/to -Wl,-Bstatic -lfoo -Wl,-Bdynamic ...

This worked most of the time, but some platforms (such as OS X) do not support the -Bstatic or equivalent flag. This made it impossible to link to the static version of a library without creating a symlink in another directory and using that one instead.

Now CMake will generate this code:

  ... /path/to/libfoo.a ...

This guarantees that the correct library is chosen. However there are some side-effects.

Missing Linker Search Directories

Projects used to be able to write this (wrong) code and it would work by accident:

  add_executable(myexe myexe.c)
  target_link_libraries(myexe /path/to/libA.so B)

where "B" is meant to link "/path/to/libB.so". This code is incorrect because it asks CMake to link to B but does not provide the proper linker search path for it. It used to work by accident because the -L/path/to would get added as part of the implementation of linking to A. The correct code would be

  link_directories(/path/to)
  add_executable(myexe myexe.c)
  target_link_libraries(myexe /path/to/libA.so B)

or even better

  add_executable(myexe myexe.c)
  target_link_libraries(myexe /path/to/libA.so /path/to/libB.so)

In order to support projects that have this bug, we've added a compatibility feature that adds the "-L/path/to" paths for all libraries linked with full paths even though the linker will not need those paths to find the main libraries. The compatibility mode is enabled when a link line contains a non-full-path library (like B) and either CMAKE_BACKWARDS_COMPATIBILITY is set to 2.4 or lower or CMAKE_LINK_OLD_PATHS is set to true.

If you are trying to build a project and run into this problem, a quick-fix is to run

 cmake -DCMAKE_LINK_OLD_PATHS:BOOL=ON .

in the top of the build tree.

Linking to System Libraries

System libraries on UNIX-like systems are typically provided in /usr/lib or /lib. These directories are considered implicit linker search paths because linkers automatically search these locations even without a flag like -L/usr/lib. Consider the code

 find_library(M_LIB m)
 target_link_libraries(myexe ${M_LIB})

Typically the find_library command would find the math library

 /usr/lib/libm.so

In CMake 2.4 and lower this value would be assigned directly to M_LIB. Then the link line generation would split off the link directory /usr/lib and the library libm.so and produce the

 ... -lm ...

Note that the -L/usr/lib option is left out because it is an implicit linker search path. The linker would see -lm and search for the math library. Typically the linker would find /usr/lib/libm.so too. However some platforms provide multiple versions of libraries correesponding to different architectures. For example, on an IRIX machine one might find the libraries

 /usr/lib/libm.so         (ELF o32)
 /usr/lib32/libm.so       (ELF n32)
 /usr/lib64/libm.so       (ELF 64)

On a Solaris machine one might find

 /usr/lib/libm.so          (sparcv8 architecture)
 /usr/lib/sparcv9/libm.so  (sparcv9 architecture)

When the linker sees -lm it in fact searches the system path corresponding to the current architecture. Internally it might use -L/usr/lib/sparcv9 instead of -L/usr/lib.

In CMake 2.6 the code

 target_link_libraries(myexe /usr/lib/libm.so)

would generate the link line

 ... /usr/lib/libm.so ...

no matter what architecture is getting linked. This might cause the linker to complain if /usr/lib/libm.so does not match the architecture it wants. This is not a problem with the link line computation. CMake is linking myexe to the library to which it was told to link.

The problem is created because find_library may not know about all the architecture-specific system search paths used by the linker. In fact when it finds /usr/lib/libm.so it may be finding a library of incorrect architecture. The solution is for the command to recognize that when it finds a library in a system search path that it should ask the linker to find the correct version of the library at link time. Consider the original example:

 find_library(M_LIB m)
 target_link_libraries(myexe ${M_LIB})

In CMake 2.6 the find_library command will set M_LIB to contain just "libm.so" when it finds /usr/lib/libm.so. The link command will then be

 target_link_libraries(myexe libm.so)

which will be converted to the link command line

 ... -lm ...

and the linker will locate the correct version of the library. If find_library does not find the library in an implicit link directory it will report the full path as usual. The user might also edit the cache to set M_LIB to a full path. In both cases the full path given to target_link_libraries will be preserved on the final link line.