diff --git a/Source/cmProjectCommand.cxx b/Source/cmProjectCommand.cxx index 1a94eb9f5d..8d362f8b05 100644 --- a/Source/cmProjectCommand.cxx +++ b/Source/cmProjectCommand.cxx @@ -202,6 +202,7 @@ bool cmProjectCommand(std::vector const& args, std::string version_string; std::array version_components; + bool has_version = prArgs.Version.has_value(); if (prArgs.Version) { if (!vx.find(*prArgs.Version)) { std::string e = @@ -267,11 +268,40 @@ bool cmProjectCommand(std::vector const& args, TopLevelCMakeVarCondSet(mf, cmStrCat("CMAKE_PROJECT_"_s, var), val); }; - createVariables("VERSION"_s, version_string); - createVariables("VERSION_MAJOR"_s, version_components[0]); - createVariables("VERSION_MINOR"_s, version_components[1]); - createVariables("VERSION_PATCH"_s, version_components[2]); - createVariables("VERSION_TWEAK"_s, version_components[3]); + // Note, this intentionally doesn't touch cache variables as the legacy + // behavior did not modify cache + auto checkAndClearVariables = [&](cm::string_view var) { + std::vector vv = { "PROJECT_", cmStrCat(projectName, "_") }; + if (mf.IsRootMakefile()) { + vv.push_back("CMAKE_PROJECT_"); + } + for (std::string const& prefix : vv) { + std::string def = cmStrCat(prefix, var); + if (!mf.GetDefinition(def).IsEmpty()) { + mf.AddDefinition(def, ""); + } + } + }; + + // TODO: We should treat VERSION the same as all other project variables, but + // setting it to empty string unconditionally causes various behavior + // changes. It needs a policy. For now, maintain the old behavior and add a + // policy in a future release. + + if (has_version) { + createVariables("VERSION"_s, version_string); + createVariables("VERSION_MAJOR"_s, version_components[0]); + createVariables("VERSION_MINOR"_s, version_components[1]); + createVariables("VERSION_PATCH"_s, version_components[2]); + createVariables("VERSION_TWEAK"_s, version_components[3]); + } else { + checkAndClearVariables("VERSION"_s); + checkAndClearVariables("VERSION_MAJOR"_s); + checkAndClearVariables("VERSION_MINOR"_s); + checkAndClearVariables("VERSION_PATCH"_s); + checkAndClearVariables("VERSION_TWEAK"_s); + } + createVariables("COMPAT_VERSION"_s, prArgs.CompatVersion.value_or("")); createVariables("DESCRIPTION"_s, prArgs.Description.value_or("")); createVariables("HOMEPAGE_URL"_s, prArgs.HomepageURL.value_or("")); diff --git a/Tests/RunCMake/project/Omissions.cmake b/Tests/RunCMake/project/Omissions.cmake index 79f695f604..d0649e7a14 100644 --- a/Tests/RunCMake/project/Omissions.cmake +++ b/Tests/RunCMake/project/Omissions.cmake @@ -14,11 +14,15 @@ function(expect_empty SUFFIX) endfunction() project(Omissions LANGUAGES) -expect_empty(VERSION) -expect_empty(VERSION_MAJOR) -expect_empty(VERSION_MINOR) -expect_empty(VERSION_PATCH) -expect_empty(VERSION_TWEAK) + +# TODO: Test these when a policy is in place to handle projects expecting +# legacy handling of the version variables +# expect_empty(VERSION) +# expect_empty(VERSION_MAJOR) +# expect_empty(VERSION_MINOR) +# expect_empty(VERSION_PATCH) +# expect_empty(VERSION_TWEAK) + expect_empty(COMPAT_VERSION) expect_empty(DESCRIPTION) expect_empty(HOMEPAGE_URL) diff --git a/Tests/RunCMake/project/RunCMakeTest.cmake b/Tests/RunCMake/project/RunCMakeTest.cmake index 92b6de593c..7376a11d8c 100644 --- a/Tests/RunCMake/project/RunCMakeTest.cmake +++ b/Tests/RunCMake/project/RunCMakeTest.cmake @@ -48,6 +48,7 @@ run_cmake(ProjectIsTopLevelMultiple) run_cmake(ProjectIsTopLevelSubdirectory) run_cmake(ProjectTwice) run_cmake(Omissions) +run_cmake(VersionAbsent) run_cmake(VersionAndLanguagesEmpty) run_cmake(VersionEmpty) run_cmake(VersionInvalid) diff --git a/Tests/RunCMake/project/VersionAbsent.cmake b/Tests/RunCMake/project/VersionAbsent.cmake new file mode 100644 index 0000000000..0091036ce7 --- /dev/null +++ b/Tests/RunCMake/project/VersionAbsent.cmake @@ -0,0 +1,14 @@ +# TODO: In the future this should be a policy test, but for now verify version +# variables remain undefined for various configurations. + +project(NoVersion LANGUAGES NONE) + +foreach(pre "NoVersion_" "PROJECT_" "CMAKE_PROJECT_") + foreach(post "" "_MAJOR" "_MINOR" "_PATCH" "_TWEAK") + if(DEFINED ${pre}VERSION${post}) + message(SEND_ERROR "${pre}VERSION${post} is defined when no project version was provided") + endif() + endforeach() +endforeach() + +add_subdirectory(VersionSubdir) diff --git a/Tests/RunCMake/project/VersionSubdir/CMakeLists.txt b/Tests/RunCMake/project/VersionSubdir/CMakeLists.txt new file mode 100644 index 0000000000..f1aeca68a3 --- /dev/null +++ b/Tests/RunCMake/project/VersionSubdir/CMakeLists.txt @@ -0,0 +1,24 @@ +project(WithVersion LANGUAGES NONE VERSION 1.1.1.1) + +project(SubdirNoVersion LANGUAGES NONE) + +foreach(post "" "_MAJOR" "_MINOR" "_PATCH" "_TWEAK") + if(NOT DEFINED PROJECT_VERSION${post}) + message(SEND_ERROR "PROJECT_VERSION${post} is not defined when previous project version was provided") + continue() + endif() + + if(NOT PROJECT_VERSION${post} STREQUAL "") + message(SEND_ERROR "PROJECT_VERSION${post} has value '${PROJECT_VERSION${post}}' when empty string is expected") + endif() +endforeach() + +if(NOT CMAKE_PROJECT_VERSION STREQUAL "1.1.1.1") + message(SEND_ERROR "CMAKE_PROJECT_VERSION has value '${CMAKE_PROJECT_VERION}' when 1.1.1.1 is expected") +endif() + +foreach(post "_MAJOR" "_MINOR" "_PATCH" "_TWEAK") + if(NOT CMAKE_PROJECT_VERSION${post} STREQUAL "1") + message(SEND_ERROR "CMAKE_PROJECT_VERSION${post} has value '${CMAKE_PROJECT_VERSION${post}}' when 1 is expected") + endif() +endforeach()