|
|
This page describes a proposal for a formal backwards/forwards
|
|
|
compatibility feature.
|
|
|
|
|
|
# Motivating Examples
|
|
|
|
|
|
## The ADD_DEFINITIONS Command
|
|
|
|
|
|
Consider code such as
|
|
|
|
|
|
add_definitions("-DFOO=\\\"hello\\ world\\\"")
|
|
|
|
|
|
which tries to add the option
|
|
|
|
|
|
-DFOO=\"hello\ world\"
|
|
|
|
|
|
to the compile command line. The code works in CMake 2.4's `Unix
|
|
|
Makefiles` generator and produces a definition as if
|
|
|
|
|
|
#define FOO "hello world"
|
|
|
|
|
|
appeared in the source code. It works only because of the way the
|
|
|
makefile generator happens to place the definition string in the command
|
|
|
line. It may not work with the VS IDE generators. In CMake HEAD we
|
|
|
provide the `COMPILE_DEFINITIONS` directory property so that one may
|
|
|
write
|
|
|
|
|
|
set_property(DIRECTORY PROPERTY COMPILE_DEFINITIONS "FOO=\"hello world\"")
|
|
|
|
|
|
to get the correct behavior on all generators. Since CMake HEAD contains
|
|
|
the appropriate escaping support it is desirable to allow the user to
|
|
|
write
|
|
|
|
|
|
add_definitions("-DFOO=\"hello world\"")
|
|
|
|
|
|
and get the expected behavior. Unfortunately if we were to start
|
|
|
escaping the definitions given to `add_definitions` we would break
|
|
|
compatibility with projects that are already adding their own escapes.
|
|
|
In hindsight we should have either supported escapes from the beginning
|
|
|
or make the command give an error that the definition value is not
|
|
|
supported, but it is too late now. A similar problem appears in the
|
|
|
`add_custom_command` command where support for properly escaping all
|
|
|
arguments was added late. The solution currently used by that command is
|
|
|
to require the user to add a `VERBATIM` argument to the command to get
|
|
|
proper escaping. Using that solution for `add_definitions` would make
|
|
|
the user write
|
|
|
|
|
|
add_definitions(VERBATIM "-DFOO=\"hello world\"")
|
|
|
|
|
|
just to get CMake to do the right thing. For compatibility CMake would
|
|
|
have to implement the wrong behavior by default forever.
|
|
|
|
|
|
## Missing Link 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)
|
|
|
|
|
|
Currently we provide the `CMAKE_OLD_LINK_PATHS` variable to allow
|
|
|
projects or users to quickly work around the problem. Full compatibility
|
|
|
would require us to support thie behavior by default forever. That would
|
|
|
allow new projects to be written with the same bug.
|
|
|
|
|
|
An alternative is to require all libraries to be linked via full path
|
|
|
(where target names are expanded automatically). Whenever a
|
|
|
non-full-path is given we could produce a warning that tells the user to
|
|
|
start using `find_library` or something like that but then implement the
|
|
|
old linker search path computation for compatibility. It is desirable to
|
|
|
let projects that have been updated for newer CMake versions tell CMake
|
|
|
that they know what they are doing and to not warn or use the
|
|
|
compatibility behavior.
|
|
|
|
|
|
# Proposed Solution
|
|
|
|
|
|
We propose the following solution to this problem.
|
|
|
|
|
|
Each change that introduces a compatibility issue is assigned a new
|
|
|
identification number (like CMP0001). Then we try to detect cases in
|
|
|
user code where it **might** be an issue and deal with it. We can
|
|
|
maintain in the implementation of CMake a mapping from policy id to a
|
|
|
rule to deal with the issue when encountered. The possible means of
|
|
|
dealing with such cases
|
|
|
are:
|
|
|
|
|
|
| Rule | Behavior | Meaning | Default? |
|
|
|
| ------------------ | -------- | ------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
|
|
|
| OLD | old | Project suppresses the diagnostic | never |
|
|
|
| NEW | new | Project declares the case has been resolved | never |
|
|
|
| WARN | old | Emit warning when case is encountered | most cases |
|
|
|
| REQUIRED_IF_USED | new | Must use the new behavior | future CMake release, compatibility can be dropped but still checked for |
|
|
|
| REQUIRED_ALWAYS | new | Must use the new behavior | future CMake release, compatibility is dropped as well as the code to check if the file uses this issue |
|
|
|
|
|
|
Several releases after a compatibility issue has been introduced we can
|
|
|
remove implementation of support for the old behavior and set it to
|
|
|
"REQUIRED" internally that requires a project to declare the issue NEW
|
|
|
or to be of a CMake version such that we know it would have generated
|
|
|
warning. See the feature lifespan example.
|
|
|
|
|
|
## Proposed CMAKE_POLICY Command
|
|
|
|
|
|
We will introduce a new command for projects to use for setting the rule
|
|
|
for each policy.
|
|
|
|
|
|
The signature
|
|
|
|
|
|
cmake_policy(<OLD|NEW> [policy-id1 [policy-id2 [...]]])
|
|
|
|
|
|
sets a list of policies to use OLD or NEW behavior. It is an error to
|
|
|
specify a policy id that does not exist (because it might refer to a
|
|
|
policy id introduced in a future version of CMake).
|
|
|
|
|
|
The signature
|
|
|
|
|
|
cmake_policy(VERSION <major[.minor[.patch]]>)
|
|
|
|
|
|
sets all rules for policies introduced in the specified version or below
|
|
|
to NEW and all other rules to WARN. It also requires the version of
|
|
|
CMake running to be at least that high. The
|
|
|
`cmake_minimum_required(VERSION)` command can do something similar (but
|
|
|
use of new commands versus fixing other issues are distinct cases so the
|
|
|
defaults may need to be different).
|
|
|
|
|
|
The signature
|
|
|
|
|
|
cmake_policy(<PUSH|POP>)
|
|
|
|
|
|
pushes or pops the current policy state on a stack. The stack is
|
|
|
automatically pushed/popped when entering a new directory (under
|
|
|
`add_subdirectory` for example). Within a directory the number of PUSH
|
|
|
and POP calls must match or it is an error. This signature is useful in
|
|
|
a .cmake script meant for inclusion in other projects. It could write
|
|
|
|
|
|
cmake_policy(PUSH)
|
|
|
cmake_policy(NEW CMP0001)
|
|
|
... code using CMP0001 ...
|
|
|
cmake_policy(POP)
|
|
|
|
|
|
The command could also provide an alias for each policy id that is more
|
|
|
human readable:
|
|
|
|
|
|
cmake_policy(NEW CMP_ESCAPE_DEFINITIONS)
|
|
|
|
|
|
## Examples
|
|
|
|
|
|
Let's define a few example policy
|
|
|
ids
|
|
|
|
|
|
| Id | Alias | Description |
|
|
|
| ------- | ------------------------------- | --------------------------------------------------------------------------------------------------------- |
|
|
|
| CMP0000 | CMP_POLICY_SPECIFICATION | Projects should specify a cmake_policy command at their top level. |
|
|
|
| CMP0001 | CMP_ESCAPE_DEFINITIONS | Enable proper escaping of definitions added with ADD_DEFINITIONS |
|
|
|
| CMP0002 | CMP_ESCAPE_CUSTOM_COMMANDS | Enable proper escaping of custom command lines with ADD_CUSTOM_COMMAND, make VERBATIM argument an error |
|
|
|
| CMP0003 | CMP_NO_AUTOMATIC_LINK_PATHS | Disable generation of compatibility -L options on link lines |
|
|
|
|
|
|
The code
|
|
|
|
|
|
add_definitions("-DFOO=\\\"hello\\ world\\\"")
|
|
|
|
|
|
can now produce a warning CMP0001 that CMake does not know whether to
|
|
|
escape the definition (only when a non-trivial value is given).
|
|
|
|
|
|
The project may suppress the warning in their release branch:
|
|
|
|
|
|
project(FOO)
|
|
|
cmake_policy(OLD CMP_ESCAPE_DEFINITIONS)
|
|
|
...
|
|
|
add_definitions("-DFOO=\\\"hello\\ world\\\"")
|
|
|
|
|
|
or fix the code:
|
|
|
|
|
|
project(FOO)
|
|
|
cmake_policy(NEW CMP_ESCAPE_DEFINITIONS)
|
|
|
...
|
|
|
add_definitions("-DFOO=\"hello world\"")
|
|
|
|
|
|
Either way CMake knows exactly how to handle the code and does not need
|
|
|
to warn.
|
|
|
|
|
|
Similarly, the
|
|
|
code
|
|
|
|
|
|
add_custom_command(OUTPUT foo.txt COMMAND mycmd -with -some -arguments -out foo.txt)
|
|
|
|
|
|
may now produce a warning CMP0002 that CMake does not know whether to
|
|
|
escape the command. The project may write
|
|
|
|
|
|
project(FOO)
|
|
|
cmake_policy(NEW CMP_ESCAPE_CUSTOM_COMMANDS)
|
|
|
...
|
|
|
add_custom_command(OUTPUT foo.txt COMMAND mycmd -with -some -arguments -out foo.txt)
|
|
|
|
|
|
to get rid of the warning and use proper escaping.
|
|
|
|
|
|
The code
|
|
|
|
|
|
target_link_libraries(mytarget /path/to/libA.so B)
|
|
|
|
|
|
may now produce a warning CMP0003 that library B may not have the
|
|
|
correct link directory specified but then generate -L/path/to anyway.
|
|
|
The project may write
|
|
|
|
|
|
project(FOO)
|
|
|
cmake_policy(NEW CMP_NO_AUTOMATIC_LINK_PATHS)
|
|
|
...
|
|
|
link_directories(/path/to)
|
|
|
target_link_libraries(mytarget /path/to/libA.so B)
|
|
|
|
|
|
Once the project has fixed all issues related to upgrading to CMake 2.6
|
|
|
it can just write
|
|
|
|
|
|
project(FOO)
|
|
|
cmake_policy(VERSION 2.6)
|
|
|
|
|
|
to get all the updated behavior. Future versions of CMake (\> 2.6) may
|
|
|
then warn on their new policies.
|
|
|
|
|
|
## Feature Lifespan Example
|
|
|
|
|
|
Using the CMP0001 || CMP_ESCAPE_DEFINITIONS example above let's
|
|
|
examine how it would work. In CMake 2.4 definitions had to be escaped by
|
|
|
the user which probably does not work on MSVC etc. So in 2.6 we want to
|
|
|
start switching over to having CMake handle the escaping.
|
|
|
|
|
|
- CMake 2.4: old behavior, issue CMP0001 does not even exist
|
|
|
|
|
|
<!-- end list -->
|
|
|
|
|
|
- CMake 2.6: issue CMP0001 is created and set to WARN and version
|
|
|
number 2.6. By default old projects will still work but will receive
|
|
|
a warning if they use this feature. The policy can be either made
|
|
|
OLD `cmake_policy(OLD CMP0001)` with no other changes to the
|
|
|
CMakeLists file or the issue can be fixed by changing their code and
|
|
|
setting it to NEW `cmake_policy(NEW CMP0001)`. Or they can ignore it
|
|
|
and just live with the warnings.
|
|
|
|
|
|
<!-- end list -->
|
|
|
|
|
|
- CMake 2.8: Nothing changes, stays as a warning
|
|
|
|
|
|
<!-- end list -->
|
|
|
|
|
|
- CMake 2.10: We change the default value of issue CMP_0001 to
|
|
|
REQUIRED_IF_USED. Implementation of the old behavior is removed,
|
|
|
but the check for its need is still present. If the user code causes
|
|
|
CMake to hit the check then it must also have specified
|
|
|
`cmake_policy(NEW CMP0001)` or `cmake_policy(VERSION 2.6)` (or
|
|
|
higher). Otherwise an error is produced.
|
|
|
|
|
|
<!-- end list -->
|
|
|
|
|
|
- CMake 2.12: We change the default value of issue CMP0001 to
|
|
|
REQUIRED_ALWAYS. Implementation of the old behavior **and** the
|
|
|
check for it are removed. The user code **must** specify
|
|
|
`cmake_policy(NEW CMP0001)` or `cmake_policy(VERSION 2.6)` (or
|
|
|
higher) whether or not the feature is used. Otherwise an error is
|
|
|
produced.
|
|
|
|
|
|
## Discussion
|
|
|
|
|
|
This solution has the following advantages:
|
|
|
|
|
|
- All compatibility issues are documented and given unique identifiers
|
|
|
- Documentation can include association with CMake version ranges
|
|
|
(when introduced, when REQUIRED, etc.)
|
|
|
- We can be more aggressive about detecting cases without worrying
|
|
|
about false positives because it is just a warning
|
|
|
- When generating a warning or error message we can reference the
|
|
|
feature id and tell the user what to do
|
|
|
- Projects can be converted to newer CMake features incrementally
|
|
|
- Project authors will be motivated by the warnings to update their
|
|
|
code, but it will build for users without extra work
|
|
|
- Code written that does not hit one of these issues will work without
|
|
|
any special declarations
|
|
|
- The hello-world example does not need to show version
|
|
|
specification
|
|
|
- We have an exit-path to remove support for a compatibility feature
|
|
|
eventually
|
|
|
- Make projects set the rule to NEW explicitly or via VERSION
|
|
|
specification
|
|
|
- Any compatibility bug introduced accidentally and reported by a user
|
|
|
can be fixed in CMake by converting it to one of these issues
|
|
|
|
|
|
Note `CMP0000` in the above example. This should be the first (lowest
|
|
|
numbered) policy we introduce. We want all projects to specify a policy
|
|
|
version level at the top:
|
|
|
|
|
|
project(FOO)
|
|
|
cmake_policy(VERSION 2.6)
|
|
|
|
|
|
This should somehow interface with `cmake_minimum_required` since many
|
|
|
projects have it already:
|
|
|
|
|
|
project(FOO)
|
|
|
cmake_minimum_required(VERSION 2.4)
|
|
|
|
|
|
We could also consider making it an optional argument to the project
|
|
|
command for brevity:
|
|
|
|
|
|
project(FOO CMAKE 2.6)
|
|
|
|
|
|
It should be a warning to encounter a command other than `project`,
|
|
|
`cmake_policy` or `cmake_minimum_required` before a policy version has
|
|
|
been set.
|
|
|
|
|
|
## Interaction with Previous Compatibility Features
|
|
|
|
|
|
CMake 2.4 currently presents all users with the cache entry
|
|
|
`CMAKE_BACKWARDS_COMPATIBILITY`. The policy mechanism replaces it, but
|
|
|
we may still need to present it for old projects and honor it for cases
|
|
|
supported by 2.4 in case projects set it. I propose the following
|
|
|
behavior
|
|
|
|
|
|
- CMake 2.6 does not present `CMAKE_BACKWARDS_COMPATIBILITY` by
|
|
|
default
|
|
|
- If the project does not set the policy version (which produces the
|
|
|
CMP0000 warning) or sets the policy version to 2.4 or lower then we
|
|
|
add `CMAKE_BACKWARDS_COMPATIBILITY` to the cache with a starting
|
|
|
value of the policy version level.
|
|
|
- It is an error for anything (project or user) to set
|
|
|
`CMAKE_BACKWARDS_COMPATIBILITY` to a value of higher than 2.4.
|
|
|
- Any check of the value performed by CMake 2.4 (for values 2.2 and
|
|
|
lower of course) is left in place.
|
|
|
- All other checks (for value 2.4) we have in CMake HEAD right now get
|
|
|
converted to policies.
|
|
|
|
|
|
There are a few other variables/properties checked by CMake HEAD that
|
|
|
approximate the policy feature.
|
|
|
|
|
|
- The variable `CMAKE_OLD_LINK_PATHS` is used for CMP0003 above.
|
|
|
- The global property `ALLOW_DUPLICATE_CUSTOM_TARGETS` is used to
|
|
|
allow multiple `ADD_CUSTOM_TARGET` commands to specify the same
|
|
|
target.
|
|
|
|
|
|
These (and others?) need to be converted to policies appropriately.
|
|
|
|
|
|
CMake 2.4 provides the `VERBATIM` option to `ADD_CUSTOM_COMMAND` to
|
|
|
enable correct escaping. This needs to be converted to a policy as
|
|
|
follows
|
|
|
|
|
|
- Old behavior is to accept VERBATIM or not and escape accordingly
|
|
|
- New behavior is to escape correctly and reject the VERBATIM option
|
|
|
|
|
|
----
|
|
|
This page was initially populated by conversion from its [original location](https://public.kitware.com/Wiki/CMake_Policies_Design_Discussion) in another wiki. |