Przeglądaj źródła

cmake_language: check CALL with control command

Fixes: #20739
Marc Chevrier 5 lat temu
rodzic
commit
12e483c563

+ 9 - 0
Help/command/cmake_language.rst

@@ -42,6 +42,15 @@ is equivalent to
 
   message(STATUS "Hello World!")
 
+.. note::
+  To ensure consistency of the code, the following commands are not allowed:
+
+  * ``if`` / ``elseif`` / ``else`` / ``endif``
+  * ``while`` / ``endwhile``
+  * ``foreach`` / ``endforeach``
+  * ``function`` / ``endfunction``
+  * ``macro`` / ``endmacro``
+
 Evaluating Code
 ^^^^^^^^^^^^^^^
 

+ 26 - 0
Source/cmCMakeLanguageCommand.cxx

@@ -3,15 +3,32 @@
 #include "cmCMakeLanguageCommand.h"
 
 #include <algorithm>
+#include <array>
 #include <cstddef>
 #include <memory>
 #include <string>
 
+#include <cm/string_view>
+#include <cmext/string_view>
+
 #include "cmExecutionStatus.h"
 #include "cmListFileCache.h"
 #include "cmMakefile.h"
 #include "cmRange.h"
 #include "cmStringAlgorithms.h"
+#include "cmSystemTools.h"
+
+namespace {
+std::array<cm::static_string_view, 12> InvalidCommands{
+  { // clang-format off
+  "function"_s, "endfunction"_s,
+  "macro"_s, "endmacro"_s,
+  "if"_s, "elseif"_s, "else"_s, "endif"_s,
+  "while"_s, "endwhile"_s,
+  "foreach"_s, "endforeach"_s
+  } // clang-format on
+};
+}
 
 bool cmCMakeLanguageCommand(std::vector<cmListFileArgument> const& args,
                             cmExecutionStatus& status)
@@ -64,6 +81,15 @@ bool cmCMakeLanguageCommand(std::vector<cmListFileArgument> const& args,
       startArg = 1;
     }
 
+    // ensure specified command is valid
+    // start/end flow control commands are not allowed
+    auto cmd = cmSystemTools::LowerCase(callCommand);
+    if (std::find(InvalidCommands.cbegin(), InvalidCommands.cend(), cmd) !=
+        InvalidCommands.cend()) {
+      status.SetError(cmStrCat("invalid command specified: "_s, callCommand));
+      return false;
+    }
+
     cmListFileFunction func;
     func.Name = callCommand;
     func.Line = context.Line;

+ 2 - 0
Tests/RunCMake/cmake_language/CallInvalidCommand.cmake

@@ -0,0 +1,2 @@
+
+cmake_language(CALL ${COMMAND})

+ 4 - 0
Tests/RunCMake/cmake_language/CheckIncludeGuard.cmake

@@ -0,0 +1,4 @@
+
+cmake_language (CALL "include_guard")
+
+set (GUARD_VALUE 1)

+ 19 - 0
Tests/RunCMake/cmake_language/CheckProject/CMakeLists.txt

@@ -0,0 +1,19 @@
+
+cmake_language (CALL cmake_minimum_required VERSION 3.17...3.18)
+
+cmake_language (CALL project CheckProject VERSION 1.2.3 LANGUAGES C)
+
+if (NOT PROJECT_NAME STREQUAL "CheckProject")
+  message (SEND_ERROR "error on project() usage.")
+endif()
+
+if (NOT CheckProject_VERSION VERSION_EQUAL "1.2.3")
+  message (SEND_ERROR "error on project() usage.")
+endif()
+
+get_property (languages GLOBAL PROPERTY ENABLED_LANGUAGES)
+if (NOT "C" IN_LIST languages)
+  message (SEND_ERROR "error on project() usage.")
+endif()
+
+add_library (lib SHARED lib.c)

+ 0 - 0
Tests/RunCMake/cmake_language/CheckProject/lib.c


+ 2 - 0
Tests/RunCMake/cmake_language/RunCMakeTest.cmake

@@ -2,6 +2,8 @@ include(RunCMake)
 
 run_cmake(no_parameters)
 run_cmake(unknown_meta_operation)
+run_cmake(call_invalid_command)
+run_cmake(call_valid_command)
 run_cmake(call_double_evaluation)
 run_cmake(call_expanded_command)
 run_cmake(call_expanded_command_and_arguments)

+ 14 - 0
Tests/RunCMake/cmake_language/call_invalid_command.cmake

@@ -0,0 +1,14 @@
+
+foreach (command IN ITEMS "function" "ENDFUNCTION"
+                          "macro" "endMACRO"
+                          "if" "elseif" "else" "endif"
+                          "while" "endwhile"
+                          "foreach" "endforeach")
+  execute_process(COMMAND "${CMAKE_COMMAND}" -DCOMMAND=${command}
+    -P "${CMAKE_CURRENT_SOURCE_DIR}/CallInvalidCommand.cmake"
+    OUTPUT_QUIET ERROR_QUIET
+    RESULT_VARIABLE result)
+  if (NOT result)
+    message (SEND_ERROR "cmake_language(CALL ${command}) unexpectedly successfull.")
+  endif()
+endforeach()

+ 99 - 0
Tests/RunCMake/cmake_language/call_valid_command.cmake

@@ -0,0 +1,99 @@
+
+## check continue() usage
+set (VALUE 0)
+foreach (i RANGE 1 4)
+  set (VALUE "${i}")
+  cmake_language (CALL "continue")
+  set (VALUE "0")
+endforeach()
+if (NOT VALUE EQUAL "4")
+  message (SEND_ERROR "error on continue() usage.")
+endif()
+
+
+## check break() usage
+set (VALUE 0)
+foreach (i RANGE 1 4)
+  set (VALUE "${i}")
+  cmake_language (CALL "break")
+  set (VALUE 0)
+endforeach()
+if (NOT VALUE EQUAL "1")
+  message (SEND_ERROR "error on break() usage.")
+endif()
+
+
+## check return() usage in macro
+macro (call_return_in_macro)
+  cmake_language (CALL "return")
+  set (VALUE 1)
+endmacro()
+function (wrapper)
+  call_return_in_macro()
+  set (VALUE 1 PARENT_SCOPE)
+endfunction()
+
+set (VALUE 0)
+wrapper()
+if (NOT VALUE EQUAL "0")
+  message (SEND_ERROR "error on return() usage in macro.")
+endif()
+
+set (VALUE 0)
+cmake_language (CALL "wrapper")
+if (NOT VALUE EQUAL "0")
+  message (SEND_ERROR "error on return() usage in macro.")
+endif()
+
+function (wrapper2)
+  cmake_language (CALL "call_return_in_macro")
+  set (VALUE 1 PARENT_SCOPE)
+endfunction()
+
+set (VALUE 0)
+wrapper2()
+if (NOT VALUE EQUAL "0")
+  message (SEND_ERROR "error on return() usage in macro.")
+endif()
+
+set (VALUE 0)
+cmake_language (CALL "wrapper2")
+if (NOT VALUE EQUAL "0")
+  message (SEND_ERROR "error on return() usage in macro.")
+endif()
+
+## check return() usage in function
+function (call_return_in_function)
+  cmake_language (CALL "return")
+  set (VALUE 1 PARENT_SCOPE)
+endfunction()
+
+set (VALUE 0)
+call_return_in_function()
+if (NOT VALUE EQUAL "0")
+  message (SEND_ERROR "error on return() usage in function.")
+endif()
+
+set (VALUE 0)
+cmake_language (CALL "call_return_in_function")
+if (NOT VALUE EQUAL "0")
+  message (SEND_ERROR "error on return() usage in function.")
+endif()
+
+
+## check usage of include_guard()
+set (GUARD_VALUE 0)
+include ("${CMAKE_CURRENT_SOURCE_DIR}/CheckIncludeGuard.cmake")
+if (NOT GUARD_VALUE EQUAL "1")
+  message (SEND_ERROR "error on include_guard() on first include.")
+endif()
+
+set (GUARD_VALUE 0)
+include ("${CMAKE_CURRENT_SOURCE_DIR}/CheckIncludeGuard.cmake")
+if (NOT GUARD_VALUE EQUAL "0")
+  message (SEND_ERROR "error on include_guard() on second include.")
+endif()
+
+
+## check usage of cmake_minimum_required() and project()
+add_subdirectory (CheckProject)