I’m trying to find a way to rename an existing CMake project, but in a way that avoids disrupting existing users. Those users will (at least initially, and for a time) be expecting the project to define the options, targets, and output variables it previously used under the old name. That situation would continue, with deprecation warnings shown on any uses of the old name, for whatever transition period is deemed necessary. Then, the project would stop creating the configuration under the previous name, and only the new name would be recognized.
The option()
s are easy enough to handle:
option(NEW_NAME_FLAG1 "Description" ON/OFF)
foreach (_option _FLAG1 ... _FLAGn)
if (DEFINED CACHE{OLD_NAME${_option}})
message(WARNING "Deprecation notice")
get_property(_help CACHE NEW_NAME${_option} PROPERTY HELPSTRING)
set(NEW_NAME${_option} "${OLD_NAME${_option}}"
CACHE BOOL "${_help}" FORCE)
unset(OLD_NAME${_option} CACHE)
endif()
endforeach()
It’s the exported config that’s proving trickier. Two basic approaches come to mind. But since I’m hitting snags with either of them, I’m curious if anyone has any recommendations or suggestions.
Option 1: Export the full config under both names
- Generate a full set of
OldNameConfig.cmake
,OldNameConfigVersion.cmake
, etc. to install inlib/cmake/OldName/
- Also generate a full set of
NewNameConfig.cmake
,NewNameConfigVersion.cmake
, etc. to install inlib/cmake/NewName/
-
OldNameConfig.cmake
should export targets in namespaceOldName::
-
NewNameConfig.cmake
should use namespaceNewName::
- Target names inside the namespace should remain unchanged. That is, the same targets should be exported in each config, just with different prefixing. However, results variables like
PackageName_INCLUDE_DIRECTORIES
should follow the config name, soOldName_INCLUDE_DIRECTORIES
vs.NewName_INCLUDE_DIRECTORIES
. - All configuration values should be identical in either config; they should be interchangeable except for the package name.
Option 2: Create a “proxy” config under the old name
- Everything would be installed as
NewNameConfig.cmake
,NewNameConfigVersion.cmake
, etc. using namespaceNewName::
, and installed tolib/cmake/NewName/
. -
OldNameConfig.cmake
, installed tolib/cmake/OldName/
, would ideally display a deprecation message, then:- Perform a
find_package(NewName ...)
on behalf of the caller, - Define a set of
ALIAS
targetsOldName::target
for eachNewName::target
- Do a
set(OldName_RESULTn "${NewName_RESULTn}")
andset(OLDNAME_RESULTn "${NEWNAME_RESULTn}")
for each result variable - End with:
find_package_handle_standard_args(OldName FOUND_VAR OldName_FOUND REQUIRED_VARS OldName_INCLUDE_DIRECTORY OldName_LIBRARY VERSION_VAR OldName_VERSION)
- Perform a
My strong preference would be the second option, primarily because the first one creates all sorts of squirrely issues, things like:
-
The package template file becomes a lot less clean; right now it outputs messages referring to the
@PACKAGE_PROJECT_NAME@::target
targets, and sets@PACKAGE_PROJECT_NAME@_VAR
variables, all of which can no longer use@PACKAGE_PROJECT_NAME@
unconditionally; it’ll depend on the namespace I’m exporting under. But then other ones, the@PACKAGE_PATH_VAR@
ones, remain unchanged.
OK, yes, I could set a variableEXPORT_NAMESPACE
for each run ofconfigure_package_config_file()
and change@PACKAGE_PROJECT_NAME@
to@PACKAGE_EXPORT_NAMESPACE@
easily enough, but… -
Maintaining two namespaces/package-names becomes especially tricky, when initially creating the config files inside the build directory.
In the existing (quite cleanly-written) tooling, each component is a target, or vice versa. Each target gets exported to its ownComponentName.cmake
file, which...Config.cmake
loads if that component is requested. Because the target names aren’t changing, I’d prefer to leave those files named the way they are. But there can’t be twoComponentName.cmake
files configured to use different namespaces, and both exported to the same build directory.
The only tricky thing about option 2 is, I’d really like OldNameConfig.cmake
to call the inner find_package(NewName ...)
with all of the same arguments that the caller used when they called find_package(OldName ...)
.
Thing is, there doesn’t seem to be a way to get at those args in their “raw” form. I’d have to process all of the various OldName_FIND_REQUIRED*
, OldName_FIND_QUIETLY
, OldName_FIND_VERSION*
, OldName_FIND_COMPONENTS
, etc. variables to reverse-engineer the calling arguments, if I wanted to call find_package(NewName ...)
the same way.
Am I (hopefully) missing something? (Either a solution to one or the other set of issues, or a completely different approach I haven’t even considered. I’m not picky.)