CMake/Policies: Difference between revisions

From KitwarePublic
Jump to navigationJump to search
No edit summary
Line 270: Line 270:
existing projects to automatically build with the old-style
existing projects to automatically build with the old-style
compatibility rules.
compatibility rules.
=Updating a Project for a new CMake Version=
When a CMake release introduces new policies it may generate warnings
for some existing projects.  These warnings indicate that changes to
the project may need to be made to deal correctly with the new
policies.  While old releases of the project can continue to build
with the warnings the project development tree should be updated to
take the new policies into account.
There are two approaches to updating a tree: one-shot and incremental.
==One-Shot Approach==
The simplest approach to updating a project for a new version of CMake
is simply to change the policy version set at the top of the project,
try building with the new CMake version, and fix problems.  For
example, to update a project to build with CMake 2.6 one might write
  cmake_minimum_required(VERSION 2.6)
at the beginning of the top-level <code>CMakeLists.txt</code> file.
This tells CMake to use the NEW behavior for every policy introduced
in CMake 2.6 and below.  When building this project with CMake 2.6 no
warnings will be produced about policies because it knows of no
policies introduced in later versions.  However, if the project was
depending on the OLD behavior of a policy it may not build since CMake
now uses the NEW behavior without warning.  It is up to the project
author who added the policy version line to fix these issues.
==Incremental Approach==

Revision as of 22:13, 10 March 2008

The CMake Policy mechanism provides backwards compatibility as a first-class feature.

Motivation

CMake is an evolving project. The developers strive to support existing projects as much as possible as changes are made. Unfortunately there are some cases where it is not possible to fix bugs and preserve backwards compatibility at the same time. We give some examples here.

Fixing an Interface Breaks Work-Arounds

Consider the add_definitions command:

 add_definitions(-DFOO)

When originally introduced the command was intended only to add simple definitions. Its implementation was simply to pass its arguments on to the compiler's command line. Since CMake supports configured header files using the configure_file command it is not necessary to pass complicated definitions on compile command lines. However, some project authors tried to do so anyway with code like

 add_definitions("-DFOO=\"some string\"")

but found that it did not work. The string

 -DFOO="some string"

would appear on the command line and the compiler would receive a definition equivalent to

 #define FOO some string

Some authors proceeded to work around the problem by adding escape sequences manually:

 add_definitions("-DFOO=\"\\\"some string\\\"\"")

The escape sequences work for some native build tools (such as Unix Makefiles) but not others. The proper way to deal with this issue was to fix the implementation in CMake to actually produce the correct escape sequences for each native build tool automatically.

Unfortunately introducing the fix would break existing projects that add their own escape sequences because the escapes themselves would be escaped. In order to support such projects no fix was introduced for years. This allowed many more projects to continue to suffer from the problem and add their own work-arounds which must now also be supported.

This problem with add_definitions is an example of a class of problems: how are we to fix an interface without breaking work-arounds for the very problem being fixed? The policy mechanism is a solution to this problem.

Changing an Implementation Breaks Projects Building Accidentally

When using CMake 2.4 or below projects may write this (wrong) code and it works 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. 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)

CMake 2.4 implemented the link to library A partly by adding -L/path/to to the linker command line. This allowed library B to be found even though no linker search path was provided for it. CMake 2.6 implements linking to library A by passing /path/to/libA.so directly to the linker as a path. This leaves out the -L/path/to which may prevent library B from being found.

While the code above leading to this problem is technically wrong it worked with a previous CMake release and needs to be supported. Therefore CMake 2.6 has support for passing the directories containing libraries whose full paths are known as linker search paths even though they are not needed for correct user code. Full compatibility would require us to support this behavior by default forever. That would allow new projects to be written with the same bug.

This problem is an example of a class of problems: how are we to fix an implementation without breaking projects depending on undocumented details of the original implementation? The policy mechanism is a solution to this problem.

Design Goals

The design goals for the CMake Policy mechanism were as follows:

  1. Existing projects should build with versions of CMake newer than that used by the project authors
    • Users should not need to edit code to get the projects to build
    • Warnings may be issued but the projects should build
  2. Correctness of new interfaces or bugs fixed in old ones should not be inhibited by compatibility requirements
    • Any reduction in correctness of the latest interface is not fair to new projects
  3. Every change to CMake that may require changes to project code should be documented
    • Each change should also have a unique identifier that can be referenced by warning and error messages
    • The new behavior is enabled only when the project has somehow indicated it is supported
  4. We must be able to eventually remove code implementing compatibility with ancient CMake versions
    • Such removal is necessary to keep the code clean and allow internal refactoring
    • After such removal attempts to build projects written for ancient versions must fail with an informative message

Solution

We've introduced the notion of a policy for dealing with changes in CMake behavior. Each policy has

  • A name of the form CMP_NNNN where NNNN is an integer identifier
  • OLD behavior that preserves compatibility with earlier versions of CMake
  • NEW behavior that is considered correct and preferred for use by new projects
  • Documentation detailing the motivation for the change and the OLD and NEW behaviors

Projects may configure the setting of each policy to request OLD or NEW behavior. When CMake encounters user code that may be affected by a particular policy it checks to see whether the project has set the policy. If the policy has been set (to OLD or NEW) then CMake follows the behavior specified. If the policy has not been set then the old behavior is used but a warning is produced telling the project author to set the policy.

Setting Policies by CMake Version

In most cases a project release should simply set a policy version corresponding to the release version of CMake for which the project is written. Setting the policy version requests NEW behavior for all policies introduced in the corresponding version of CMake or earlier. Policies introduced in later versions are marked as not set in order to produce proper warning messages.

The policy version is set using the cmake_policy command's VERSION signature. For example, the code

 cmake_policy(VERSION 2.6)

will request NEW behavior for all policies introduced in CMake 2.6 or earlier. The cmake_minimum_required command will also set the policy version which is convenient for use at the top of projects. A project should typically begin with the lines

 cmake_minimum_required(VERSION 2.6)
 project(MyProject)
 # ...code using CMake 2.6 policies

Of course one should replace "2.6" with a higher version as necessary.

When a new version of CMake is released that introduces new policies it will still build old projects because they do not request NEW behavior for any of the new policies. When starting a new project one should always specify the most recent release of CMake to be supported as the policy version level. This will make sure that the project is written to work using policies from that version of CMake and not using any old behavior.

Policy CMP_0000 has been introduced to require all projects to specify a policy version in their top-level CMakeLists.txt file. If no policy version is set CMake will warn and assume a policy version of 2.4. This allows existing projects that do not specify cmake_minimum_required to build as they would have with CMake 2.4.

Setting Policies Individually

Each policy may be set individually to help project authors incrementally convert their projects to use new behavior or silence warnings about dependence on old behavior. The cmake_policy command's SET signature may be used to explicitly request OLD or NEW behavior for a particular policy.

For example, CMake 2.6 introduces policy CMP_0002 which requires all logical target names to be globally unique (duplicate target names previously worked in some cases by accident but were not diagnosed). Projects using duplicate target names and working accidentally will receive warnings referencing the policy. The warnings may be silenced by the code

 cmake_policy(SET CMP_0002 OLD)

which explicitly tells CMake to use OLD behavior for the policy (silently accept duplicate target names). Another option is to use the code

 cmake_policy(SET CMP_0002 NEW)

to explicitly tell CMake to use NEW behavior (produce an error when a duplicate target is created). Once this is added to the project it will not build until the author removes the duplicate targets.

Policy Stack

Policy settings are scoped using a stack. A new level of the stack is pushed when entering a new subdirectory of the project (with add_subdirectory) and popped when leaving it. Therefore setting a policy in one directory of a project will not affect parent or sibling directories but will affect subdirectories.

This is useful when a project contains subprojects maintained separately but built inside the tree. The top-level CMakeLists.txt file in a project may write

 cmake_policy(VERSION 2.6)
 project(MyProject)
 add_subdirectory(OtherProject)
 # ... code requiring new behavior as of CMake 2.6 ...

while the OtherProject/CMakeLists.txt file contains

 cmake_policy(VERSION 2.4)
 project(OtherProject)
 # ... code that buidls with CMake 2.4 ...

This allows the main project to be updated to CMake 2.6 while the subproject continues to build with CMake 2.4 until its maintainers update it.

User code may use the cmake_policy command to PUSH and POP its own stack levels as long as every PUSH must be paired with a POP. This is useful to temporarily request different behavior for a small section of code. For example, policy CMP_0003 removes extra link directories that used to be included when NEW behavior is used. While incrementally updating a project it may be difficult to build a particular target with the NEW behavior but all other targets are okay. The code

 cmake_policy(PUSH)
 cmake_policy(SET CMP_0003 OLD) # use old-style link directories for now
 add_executable(myexe ...)
 cmake_policy(POP)

will silence the warning and use the OLD behavior for that target.

Interaction with Previous Compatibility Mechanisms

CMake 2.4 and below dealt with backwards compatibility by providing the CMAKE_BACKWARDS_COMPATIBILITY variable as a cache entry. The variable could be set by the user when building a project to tell CMake to try to support an older version. This allowed users to build older projects but only if they knew how to set the variable. In some cases CMake could generate an error about old behavior and tell the user to set the variable but in other cases it would silently fail or produce errors not mentioning the variable.

The main problem with the CMAKE_BACKWARDS_COMPATIBILITY was that it did not distinguish between a user trying to build someone else's project and that project's author. Only project authors should be required to do anything that changes how their project builds with new CMake versions. The CMake Policy mechanism addresses this issue.

The CMake Policy mechanism was introduced in version CMake 2.6. For maximum compatibility CMake does not try to retroactively convert behavior changes introduced in versions 2.4 or lower into policies. Instead policy CMP_0001 decides whether or not to support 2.4-style compatibility. It's OLD behavior is to present CMAKE_BACKWARDS_COMPATIBILITY and check for settings lower than 2.4. It's NEW behavior is to not add CMAKE_BACKWARDS_COMPATIBILITY and not check its value therefore removing all compatibility with versions lower than 2.4.

Since all policies are introduced in CMake version 2.6 or later it does not make sense to allow a policy version lower than 2.4 to be set. Therefore the cmake_policy command's VERSION argument may not be lower than 2.4. Similarly, cmake_minimum_required will never set the policy version lower than 2.4 no matter what version is specified. This allows existing projects to automatically build with the old-style compatibility rules.

Updating a Project for a new CMake Version

When a CMake release introduces new policies it may generate warnings for some existing projects. These warnings indicate that changes to the project may need to be made to deal correctly with the new policies. While old releases of the project can continue to build with the warnings the project development tree should be updated to take the new policies into account.

There are two approaches to updating a tree: one-shot and incremental.

One-Shot Approach

The simplest approach to updating a project for a new version of CMake is simply to change the policy version set at the top of the project, try building with the new CMake version, and fix problems. For example, to update a project to build with CMake 2.6 one might write

 cmake_minimum_required(VERSION 2.6)

at the beginning of the top-level CMakeLists.txt file. This tells CMake to use the NEW behavior for every policy introduced in CMake 2.6 and below. When building this project with CMake 2.6 no warnings will be produced about policies because it knows of no policies introduced in later versions. However, if the project was depending on the OLD behavior of a policy it may not build since CMake now uses the NEW behavior without warning. It is up to the project author who added the policy version line to fix these issues.

Incremental Approach