Browse Source

Merge topic 'function-var-current'

24fdd51f45 Refactor: Replace CMAKE_CURRENT_LIST_DIR with CMAKE_CURRENT_FUNCTION_LIST_DIR
90e3e2a777 cmFunctionCommand: Introduce `CMAKE_CURRENT_FUNCTION*` variables
dd54290dab Refactor: Modernize `function` command

Acked-by: Kitware Robot <[email protected]>
Acked-by: Ben Boeckel <[email protected]>
Merge-request: !4000
Kyle Edwards 5 years ago
parent
commit
966a9eece3

+ 6 - 0
Help/command/macro.rst

@@ -91,6 +91,12 @@ just terminate execution of the macro; rather, control is returned
 from the scope of the macro call.  To avoid confusion, it is recommended
 from the scope of the macro call.  To avoid confusion, it is recommended
 to avoid :command:`return()` in macros altogether.
 to avoid :command:`return()` in macros altogether.
 
 
+Unlike a function, the :variable:`CMAKE_CURRENT_FUNCTION`,
+:variable:`CMAKE_CURRENT_FUNCTION_LIST_DIR`,
+:variable:`CMAKE_CURRENT_FUNCTION_LIST_FILE`,
+:variable:`CMAKE_CURRENT_FUNCTION_LIST_LINE` variables are not
+set for macro.
+
 .. _`Argument Caveats`:
 .. _`Argument Caveats`:
 
 
 Argument Caveats
 Argument Caveats

+ 4 - 0
Help/manual/cmake-variables.7.rst

@@ -37,6 +37,10 @@ Variables that Provide Information
    /variable/CMAKE_CROSSCOMPILING_EMULATOR
    /variable/CMAKE_CROSSCOMPILING_EMULATOR
    /variable/CMAKE_CTEST_COMMAND
    /variable/CMAKE_CTEST_COMMAND
    /variable/CMAKE_CURRENT_BINARY_DIR
    /variable/CMAKE_CURRENT_BINARY_DIR
+   /variable/CMAKE_CURRENT_FUNCTION
+   /variable/CMAKE_CURRENT_FUNCTION_LIST_DIR
+   /variable/CMAKE_CURRENT_FUNCTION_LIST_FILE
+   /variable/CMAKE_CURRENT_FUNCTION_LIST_LINE
    /variable/CMAKE_CURRENT_LIST_DIR
    /variable/CMAKE_CURRENT_LIST_DIR
    /variable/CMAKE_CURRENT_LIST_FILE
    /variable/CMAKE_CURRENT_LIST_FILE
    /variable/CMAKE_CURRENT_LIST_LINE
    /variable/CMAKE_CURRENT_LIST_LINE

+ 9 - 0
Help/release/dev/CMAKE_CURRENT_FUNCTION.rst

@@ -0,0 +1,9 @@
+CMAKE_CURRENT_FUNCTION
+----------------------
+
+* Define the following variables inside a function:
+
+    - :variable:`CMAKE_CURRENT_FUNCTION`
+    - :variable:`CMAKE_CURRENT_FUNCTION_LIST_DIR`
+    - :variable:`CMAKE_CURRENT_FUNCTION_LIST_FILE`
+    - :variable:`CMAKE_CURRENT_FUNCTION_LIST_LINE`

+ 6 - 0
Help/variable/CMAKE_CURRENT_FUNCTION.rst

@@ -0,0 +1,6 @@
+CMAKE_CURRENT_FUNCTION
+----------------------
+
+When executing code inside a :command:`function`, this variable
+contains the name of the current function.  It can be used for
+diagnostic or debug messages.

+ 33 - 0
Help/variable/CMAKE_CURRENT_FUNCTION_LIST_DIR.rst

@@ -0,0 +1,33 @@
+CMAKE_CURRENT_FUNCTION_LIST_DIR
+-------------------------------
+
+When executing code inside a :command:`function`, this variable
+contains the full directory of the listfile defining the current function.
+
+It is quite common practice in CMake that modules use some additional files
+(e.g., templates to render).  And the code typically did the following:
+
+.. code-block:: cmake
+    :caption: Bad
+
+    set(_THIS_MODULE_BASE_DIR "${CMAKE_CURRENT_LIST_DIR}")
+
+    function(foo)
+      configure_file(
+        "${_THIS_MODULE_BASE_DIR}/some.template.in"
+        some.output
+      )
+    endfunction()
+
+Using this variable inside a function eliminates the neccessity of the
+additional one with "global" scope:
+
+.. code-block:: cmake
+    :caption: Good
+
+    function(foo)
+      configure_file(
+        "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/some.template.in"
+        some.output
+      )
+    endfunction()

+ 5 - 0
Help/variable/CMAKE_CURRENT_FUNCTION_LIST_FILE.rst

@@ -0,0 +1,5 @@
+CMAKE_CURRENT_FUNCTION_LIST_FILE
+--------------------------------
+
+When executing code inside a :command:`function`, this variable
+contains the full path to the listfile declaring a current function.

+ 5 - 0
Help/variable/CMAKE_CURRENT_FUNCTION_LIST_LINE.rst

@@ -0,0 +1,5 @@
+CMAKE_CURRENT_FUNCTION_LIST_LINE
+--------------------------------
+
+When executing code inside a :command:`function`, this variable
+contains the line number in the listfile where a current function has defined.

+ 1 - 3
Modules/AndroidTestUtilities.cmake

@@ -76,8 +76,6 @@ Module Functions
 
 
 include(${CMAKE_CURRENT_LIST_DIR}/ExternalData.cmake)
 include(${CMAKE_CURRENT_LIST_DIR}/ExternalData.cmake)
 
 
-set(_AndroidTestUtilities_SELF_DIR "${CMAKE_CURRENT_LIST_DIR}")
-
 # The parameters to this function should be set to the list of directories,
 # The parameters to this function should be set to the list of directories,
 # files, and libraries that need to be installed prior to testing.
 # files, and libraries that need to be installed prior to testing.
 function(android_add_test_data test_name)
 function(android_add_test_data test_name)
@@ -159,6 +157,6 @@ function(android_add_test_data test_name)
       "-Darg_files=${processed_FILES}"
       "-Darg_files=${processed_FILES}"
       "-Darg_libs=${AST_LIBS}"
       "-Darg_libs=${AST_LIBS}"
       "-Darg_src_dir=${CMAKE_CURRENT_SOURCE_DIR}"
       "-Darg_src_dir=${CMAKE_CURRENT_SOURCE_DIR}"
-      -P ${_AndroidTestUtilities_SELF_DIR}/AndroidTestUtilities/PushToAndroidDevice.cmake)
+      -P ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/AndroidTestUtilities/PushToAndroidDevice.cmake)
   endif()
   endif()
 endfunction()
 endfunction()

+ 2 - 3
Modules/CMakeAddFortranSubdirectory.cmake

@@ -43,7 +43,6 @@ future version that supports installation of the external project
 binaries during ``make install``.
 binaries during ``make install``.
 #]=======================================================================]
 #]=======================================================================]
 
 
-set(_MS_MINGW_SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR})
 include(CheckLanguage)
 include(CheckLanguage)
 include(ExternalProject)
 include(ExternalProject)
 
 
@@ -87,11 +86,11 @@ function(_setup_mingw_config_and_build source_dir build_dir)
   file(TO_NATIVE_PATH "${MINGW_PATH}" MINGW_PATH)
   file(TO_NATIVE_PATH "${MINGW_PATH}" MINGW_PATH)
   string(REPLACE "\\" "\\\\" MINGW_PATH "${MINGW_PATH}")
   string(REPLACE "\\" "\\\\" MINGW_PATH "${MINGW_PATH}")
   configure_file(
   configure_file(
-    ${_MS_MINGW_SOURCE_DIR}/CMakeAddFortranSubdirectory/config_mingw.cmake.in
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/CMakeAddFortranSubdirectory/config_mingw.cmake.in
     ${build_dir}/config_mingw.cmake
     ${build_dir}/config_mingw.cmake
     @ONLY)
     @ONLY)
   configure_file(
   configure_file(
-    ${_MS_MINGW_SOURCE_DIR}/CMakeAddFortranSubdirectory/build_mingw.cmake.in
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/CMakeAddFortranSubdirectory/build_mingw.cmake.in
     ${build_dir}/build_mingw.cmake
     ${build_dir}/build_mingw.cmake
     @ONLY)
     @ONLY)
 endfunction()
 endfunction()

+ 1 - 2
Modules/DeployQt4.cmake

@@ -106,7 +106,6 @@ and plugin installation.  See documentation of FIXUP_QT4_BUNDLE.
 # The functions defined in this file depend on the fixup_bundle function
 # The functions defined in this file depend on the fixup_bundle function
 # (and others) found in BundleUtilities.cmake
 # (and others) found in BundleUtilities.cmake
 
 
-set(DeployQt4_cmake_dir "${CMAKE_CURRENT_LIST_DIR}")
 set(DeployQt4_apple_plugins_dir "PlugIns")
 set(DeployQt4_apple_plugins_dir "PlugIns")
 
 
 function(write_qt4_conf qt_conf_dir qt_conf_contents)
 function(write_qt4_conf qt_conf_dir qt_conf_contents)
@@ -392,7 +391,7 @@ function(install_qt4_executable executable)
   resolve_qt4_paths(libs "")
   resolve_qt4_paths(libs "")
 
 
   install(CODE
   install(CODE
-"include(\"${DeployQt4_cmake_dir}/DeployQt4.cmake\")
+"include(\"${CMAKE_CURRENT_FUNCTION_LIST_DIR}/DeployQt4.cmake\")
 set(BU_CHMOD_BUNDLE_ITEMS TRUE)
 set(BU_CHMOD_BUNDLE_ITEMS TRUE)
 FIXUP_QT4_EXECUTABLE(\"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${executable}\" \"\" \"${libs}\" \"${dirs}\" \"${plugins_dir}\" \"${request_qt_conf}\")"
 FIXUP_QT4_EXECUTABLE(\"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${executable}\" \"\" \"${libs}\" \"${dirs}\" \"${plugins_dir}\" \"${request_qt_conf}\")"
           ${component}
           ${component}

+ 1 - 4
Modules/FetchContent.cmake

@@ -596,9 +596,6 @@ current working directory.
 
 
 #]=======================================================================]
 #]=======================================================================]
 
 
-
-set(__FetchContent_privateDir "${CMAKE_CURRENT_LIST_DIR}/FetchContent")
-
 #=======================================================================
 #=======================================================================
 # Recording and retrieving content details for later population
 # Recording and retrieving content details for later population
 #=======================================================================
 #=======================================================================
@@ -888,7 +885,7 @@ function(__FetchContent_directPopulate contentName)
   # anything to be updated, so extra rebuilds of the project won't occur.
   # anything to be updated, so extra rebuilds of the project won't occur.
   # Make sure to pass through CMAKE_MAKE_PROGRAM in case the main project
   # Make sure to pass through CMAKE_MAKE_PROGRAM in case the main project
   # has this set to something not findable on the PATH.
   # has this set to something not findable on the PATH.
-  configure_file("${__FetchContent_privateDir}/CMakeLists.cmake.in"
+  configure_file("${CMAKE_CURRENT_FUNCTION_LIST_DIR}/FetchContent/CMakeLists.cmake.in"
                  "${ARG_SUBBUILD_DIR}/CMakeLists.txt")
                  "${ARG_SUBBUILD_DIR}/CMakeLists.txt")
   execute_process(
   execute_process(
     COMMAND ${CMAKE_COMMAND} ${generatorOpts} .
     COMMAND ${CMAKE_COMMAND} ${generatorOpts} .

+ 1 - 2
Modules/UseJava.cmake

@@ -405,7 +405,6 @@ endfunction()
 
 
 # define helper scripts
 # define helper scripts
 set(_JAVA_EXPORT_TARGETS_SCRIPT ${CMAKE_CURRENT_LIST_DIR}/javaTargets.cmake.in)
 set(_JAVA_EXPORT_TARGETS_SCRIPT ${CMAKE_CURRENT_LIST_DIR}/javaTargets.cmake.in)
-set(_JAVA_CLASS_FILELIST_SCRIPT ${CMAKE_CURRENT_LIST_DIR}/UseJavaClassFilelist.cmake)
 set(_JAVA_SYMLINK_SCRIPT ${CMAKE_CURRENT_LIST_DIR}/UseJavaSymlinks.cmake)
 set(_JAVA_SYMLINK_SCRIPT ${CMAKE_CURRENT_LIST_DIR}/UseJavaSymlinks.cmake)
 
 
 if (CMAKE_HOST_WIN32 AND NOT CYGWIN AND CMAKE_HOST_SYSTEM_NAME MATCHES "Windows")
 if (CMAKE_HOST_WIN32 AND NOT CYGWIN AND CMAKE_HOST_SYSTEM_NAME MATCHES "Windows")
@@ -627,7 +626,7 @@ function(add_jar _TARGET_NAME)
             COMMAND ${CMAKE_COMMAND}
             COMMAND ${CMAKE_COMMAND}
                 -DCMAKE_JAVA_CLASS_OUTPUT_PATH=${CMAKE_JAVA_CLASS_OUTPUT_PATH}
                 -DCMAKE_JAVA_CLASS_OUTPUT_PATH=${CMAKE_JAVA_CLASS_OUTPUT_PATH}
                 -DCMAKE_JAR_CLASSES_PREFIX="${CMAKE_JAR_CLASSES_PREFIX}"
                 -DCMAKE_JAR_CLASSES_PREFIX="${CMAKE_JAR_CLASSES_PREFIX}"
-                -P ${_JAVA_CLASS_FILELIST_SCRIPT}
+                -P ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/UseJavaClassFilelist.cmake
             DEPENDS ${CMAKE_JAVA_CLASS_OUTPUT_PATH}/java_compiled_${_TARGET_NAME}
             DEPENDS ${CMAKE_JAVA_CLASS_OUTPUT_PATH}/java_compiled_${_TARGET_NAME}
             WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
             WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
         )
         )

+ 51 - 27
Source/cmFunctionCommand.cxx

@@ -2,7 +2,6 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmFunctionCommand.h"
 #include "cmFunctionCommand.h"
 
 
-#include <sstream>
 #include <utility>
 #include <utility>
 
 
 #include <cm/memory>
 #include <cm/memory>
@@ -19,8 +18,20 @@
 #include "cmRange.h"
 #include "cmRange.h"
 #include "cmState.h"
 #include "cmState.h"
 #include "cmStringAlgorithms.h"
 #include "cmStringAlgorithms.h"
+#include "cmSystemTools.h"
 
 
 namespace {
 namespace {
+std::string const ARGC = "ARGC";
+std::string const ARGN = "ARGN";
+std::string const ARGV = "ARGV";
+std::string const CMAKE_CURRENT_FUNCTION = "CMAKE_CURRENT_FUNCTION";
+std::string const CMAKE_CURRENT_FUNCTION_LIST_FILE =
+  "CMAKE_CURRENT_FUNCTION_LIST_FILE";
+std::string const CMAKE_CURRENT_FUNCTION_LIST_DIR =
+  "CMAKE_CURRENT_FUNCTION_LIST_DIR";
+std::string const CMAKE_CURRENT_FUNCTION_LIST_LINE =
+  "CMAKE_CURRENT_FUNCTION_LIST_LINE";
+
 // define the class for function commands
 // define the class for function commands
 class cmFunctionHelperCommand
 class cmFunctionHelperCommand
 {
 {
@@ -36,8 +47,8 @@ public:
   std::vector<cmListFileFunction> Functions;
   std::vector<cmListFileFunction> Functions;
   cmPolicies::PolicyMap Policies;
   cmPolicies::PolicyMap Policies;
   std::string FilePath;
   std::string FilePath;
+  long Line;
 };
 };
-}
 
 
 bool cmFunctionHelperCommand::operator()(
 bool cmFunctionHelperCommand::operator()(
   std::vector<cmListFileArgument> const& args,
   std::vector<cmListFileArgument> const& args,
@@ -52,9 +63,9 @@ bool cmFunctionHelperCommand::operator()(
   // make sure the number of arguments passed is at least the number
   // make sure the number of arguments passed is at least the number
   // required by the signature
   // required by the signature
   if (expandedArgs.size() < this->Args.size() - 1) {
   if (expandedArgs.size() < this->Args.size() - 1) {
-    std::string errorMsg = cmStrCat(
+    auto const errorMsg = cmStrCat(
       "Function invoked with incorrect arguments for function named: ",
       "Function invoked with incorrect arguments for function named: ",
-      this->Args[0]);
+      this->Args.front());
     inStatus.SetError(errorMsg);
     inStatus.SetError(errorMsg);
     return false;
     return false;
   }
   }
@@ -63,30 +74,40 @@ bool cmFunctionHelperCommand::operator()(
                                             this->Policies);
                                             this->Policies);
 
 
   // set the value of argc
   // set the value of argc
-  makefile.AddDefinition("ARGC", std::to_string(expandedArgs.size()));
-  makefile.MarkVariableAsUsed("ARGC");
+  makefile.AddDefinition(ARGC, std::to_string(expandedArgs.size()));
+  makefile.MarkVariableAsUsed(ARGC);
 
 
   // set the values for ARGV0 ARGV1 ...
   // set the values for ARGV0 ARGV1 ...
-  for (unsigned int t = 0; t < expandedArgs.size(); ++t) {
-    std::ostringstream tmpStream;
-    tmpStream << "ARGV" << t;
-    makefile.AddDefinition(tmpStream.str(), expandedArgs[t]);
-    makefile.MarkVariableAsUsed(tmpStream.str());
+  for (auto t = 0u; t < expandedArgs.size(); ++t) {
+    auto const value = cmStrCat(ARGV, std::to_string(t));
+    makefile.AddDefinition(value, expandedArgs[t]);
+    makefile.MarkVariableAsUsed(value);
   }
   }
 
 
   // define the formal arguments
   // define the formal arguments
-  for (unsigned int j = 1; j < this->Args.size(); ++j) {
+  for (auto j = 1u; j < this->Args.size(); ++j) {
     makefile.AddDefinition(this->Args[j], expandedArgs[j - 1]);
     makefile.AddDefinition(this->Args[j], expandedArgs[j - 1]);
   }
   }
 
 
   // define ARGV and ARGN
   // define ARGV and ARGN
-  std::string argvDef = cmJoin(expandedArgs, ";");
-  auto eit = expandedArgs.begin() + (this->Args.size() - 1);
-  std::string argnDef = cmJoin(cmMakeRange(eit, expandedArgs.end()), ";");
-  makefile.AddDefinition("ARGV", argvDef);
-  makefile.MarkVariableAsUsed("ARGV");
-  makefile.AddDefinition("ARGN", argnDef);
-  makefile.MarkVariableAsUsed("ARGN");
+  auto const argvDef = cmJoin(expandedArgs, ";");
+  auto const eit = expandedArgs.begin() + (this->Args.size() - 1);
+  auto const argnDef = cmJoin(cmMakeRange(eit, expandedArgs.end()), ";");
+  makefile.AddDefinition(ARGV, argvDef);
+  makefile.MarkVariableAsUsed(ARGV);
+  makefile.AddDefinition(ARGN, argnDef);
+  makefile.MarkVariableAsUsed(ARGN);
+
+  makefile.AddDefinition(CMAKE_CURRENT_FUNCTION, this->Args.front());
+  makefile.MarkVariableAsUsed(CMAKE_CURRENT_FUNCTION);
+  makefile.AddDefinition(CMAKE_CURRENT_FUNCTION_LIST_FILE, this->FilePath);
+  makefile.MarkVariableAsUsed(CMAKE_CURRENT_FUNCTION_LIST_FILE);
+  makefile.AddDefinition(CMAKE_CURRENT_FUNCTION_LIST_DIR,
+                         cmSystemTools::GetFilenamePath(this->FilePath));
+  makefile.MarkVariableAsUsed(CMAKE_CURRENT_FUNCTION_LIST_DIR);
+  makefile.AddDefinition(CMAKE_CURRENT_FUNCTION_LIST_LINE,
+                         std::to_string(this->Line));
+  makefile.MarkVariableAsUsed(CMAKE_CURRENT_FUNCTION_LIST_LINE);
 
 
   // Invoke all the functions that were collected in the block.
   // Invoke all the functions that were collected in the block.
   // for each function
   // for each function
@@ -100,7 +121,7 @@ bool cmFunctionHelperCommand::operator()(
       return false;
       return false;
     }
     }
     if (status.GetReturnInvoked()) {
     if (status.GetReturnInvoked()) {
-      return true;
+      break;
     }
     }
   }
   }
 
 
@@ -129,7 +150,8 @@ bool cmFunctionFunctionBlocker::ArgumentsMatch(cmListFileFunction const& lff,
   std::vector<std::string> expandedArguments;
   std::vector<std::string> expandedArguments;
   mf.ExpandArguments(lff.Arguments, expandedArguments,
   mf.ExpandArguments(lff.Arguments, expandedArguments,
                      this->GetStartingContext().FilePath.c_str());
                      this->GetStartingContext().FilePath.c_str());
-  return expandedArguments.empty() || expandedArguments[0] == this->Args[0];
+  return expandedArguments.empty() ||
+    expandedArguments.front() == this->Args.front();
 }
 }
 
 
 bool cmFunctionFunctionBlocker::Replay(
 bool cmFunctionFunctionBlocker::Replay(
@@ -141,11 +163,14 @@ bool cmFunctionFunctionBlocker::Replay(
   f.Args = this->Args;
   f.Args = this->Args;
   f.Functions = std::move(functions);
   f.Functions = std::move(functions);
   f.FilePath = this->GetStartingContext().FilePath;
   f.FilePath = this->GetStartingContext().FilePath;
+  f.Line = this->GetStartingContext().Line;
   mf.RecordPolicies(f.Policies);
   mf.RecordPolicies(f.Policies);
-  mf.GetState()->AddScriptedCommand(this->Args[0], std::move(f));
+  mf.GetState()->AddScriptedCommand(this->Args.front(), std::move(f));
   return true;
   return true;
 }
 }
 
 
+} // anonymous namespace
+
 bool cmFunctionCommand(std::vector<std::string> const& args,
 bool cmFunctionCommand(std::vector<std::string> const& args,
                        cmExecutionStatus& status)
                        cmExecutionStatus& status)
 {
 {
@@ -155,10 +180,9 @@ bool cmFunctionCommand(std::vector<std::string> const& args,
   }
   }
 
 
   // create a function blocker
   // create a function blocker
-  {
-    auto fb = cm::make_unique<cmFunctionFunctionBlocker>();
-    cmAppend(fb->Args, args);
-    status.GetMakefile().AddFunctionBlocker(std::move(fb));
-  }
+  auto fb = cm::make_unique<cmFunctionFunctionBlocker>();
+  cmAppend(fb->Args, args);
+  status.GetMakefile().AddFunctionBlocker(std::move(fb));
+
   return true;
   return true;
 }
 }

+ 1 - 0
Tests/RunCMake/CMakeLists.txt

@@ -272,6 +272,7 @@ add_RunCMake_test(find_package)
 add_RunCMake_test(find_path)
 add_RunCMake_test(find_path)
 add_RunCMake_test(find_program -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME})
 add_RunCMake_test(find_program -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME})
 add_RunCMake_test(foreach)
 add_RunCMake_test(foreach)
+add_RunCMake_test(function)
 add_RunCMake_test(get_filename_component)
 add_RunCMake_test(get_filename_component)
 add_RunCMake_test(get_property)
 add_RunCMake_test(get_property)
 add_RunCMake_test(if)
 add_RunCMake_test(if)

+ 7 - 0
Tests/RunCMake/function/CMAKE_CURRENT_FUNCTION-stdout.txt

@@ -0,0 +1,7 @@
+function\(print_self\)
+    file\(STRINGS "\${CMAKE_CURRENT_FUNCTION_LIST_FILE}" _lines\)
+    math\(EXPR _begin "\${CMAKE_CURRENT_FUNCTION_LIST_LINE} - 1"\)
+    list\(SUBLIST _lines \${_begin} 7 _lines\) # This function has 7 lines only
+    list\(JOIN _lines "\\n" _lines\)
+    message\(STATUS "Print the `\${CMAKE_CURRENT_FUNCTION}` function:\\n\${_lines}"\)
+endfunction\(\)

+ 94 - 0
Tests/RunCMake/function/CMAKE_CURRENT_FUNCTION.cmake

@@ -0,0 +1,94 @@
+set(_THIS_FILE "${CMAKE_CURRENT_LIST_FILE}")
+set(_THIS_DIR "${CMAKE_CURRENT_LIST_DIR}")
+
+if(CMAKE_CURRENT_FUNCTION)
+  message(SEND_ERROR "`CMAKE_CURRENT_FUNCTION` is not expected to be set here")
+endif()
+if(CMAKE_CURRENT_FUNCTION_LIST_FILE)
+  message(SEND_ERROR "`CMAKE_CURRENT_FUNCTION_LIST_FILE` is not expected to be set here")
+endif()
+if(CMAKE_CURRENT_FUNCTION_LIST_DIR)
+  message(SEND_ERROR "`CMAKE_CURRENT_FUNCTION_LIST_DIR` is not expected to be set here")
+endif()
+if(CMAKE_CURRENT_FUNCTION_LIST_LINE)
+  message(SEND_ERROR "`CMAKE_CURRENT_FUNCTION_LIST_LINE` is not expected to be set here")
+endif()
+
+function(bar)
+  if(NOT CMAKE_CURRENT_FUNCTION STREQUAL "bar")
+    message(SEND_ERROR "Bad value of `CMAKE_CURRENT_FUNCTION`")
+  endif()
+  if(NOT CMAKE_CURRENT_FUNCTION_LIST_FILE MATCHES "^.*/CMAKE_CURRENT_FUNCTION.cmake$")
+    message(SEND_ERROR "Bad value of `CMAKE_CURRENT_FUNCTION_LIST_FILE`")
+  endif()
+  if(NOT CMAKE_CURRENT_FUNCTION_LIST_FILE STREQUAL _THIS_FILE)
+    message(SEND_ERROR "Bad value of `CMAKE_CURRENT_FUNCTION_LIST_FILE`")
+  endif()
+  if(NOT CMAKE_CURRENT_FUNCTION_LIST_DIR MATCHES "^.*/Tests/RunCMake/function$")
+    message(SEND_ERROR "Bad value of `CMAKE_CURRENT_FUNCTION_LIST_DIR`")
+  endif()
+  if(NOT CMAKE_CURRENT_FUNCTION_LIST_DIR STREQUAL _THIS_DIR)
+    message(SEND_ERROR "Bad value of `CMAKE_CURRENT_FUNCTION_LIST_DIR`")
+  endif()
+  if(NOT CMAKE_CURRENT_FUNCTION_LIST_LINE EQUAL 17)
+    message(SEND_ERROR "Bad value of `CMAKE_CURRENT_FUNCTION_LIST_LINE`")
+  endif()
+endfunction()
+
+function(foo)
+  if(NOT CMAKE_CURRENT_FUNCTION STREQUAL "foo")
+    message(SEND_ERROR "Bad value of `CMAKE_CURRENT_FUNCTION`")
+  endif()
+  if(NOT CMAKE_CURRENT_FUNCTION_LIST_FILE MATCHES "^.*/function/CMAKE_CURRENT_FUNCTION.cmake$")
+    message(SEND_ERROR "Bad value of `CMAKE_CURRENT_FUNCTION_LIST_FILE`")
+  endif()
+  if(NOT CMAKE_CURRENT_FUNCTION_LIST_FILE STREQUAL _THIS_FILE)
+    message(SEND_ERROR "Bad value of `CMAKE_CURRENT_FUNCTION_LIST_FILE`")
+  endif()
+  if(NOT CMAKE_CURRENT_FUNCTION_LIST_DIR MATCHES "^.*/Tests/RunCMake/function$")
+    message(SEND_ERROR "Bad value of `CMAKE_CURRENT_FUNCTION_LIST_DIR`")
+  endif()
+  if(NOT CMAKE_CURRENT_FUNCTION_LIST_LINE EQUAL 38)
+    message(SEND_ERROR "Bad value of `CMAKE_CURRENT_FUNCTION_LIST_LINE`")
+  endif()
+  if(NOT CMAKE_CURRENT_FUNCTION_LIST_DIR STREQUAL _THIS_DIR)
+    message(SEND_ERROR "Bad value of `CMAKE_CURRENT_FUNCTION_LIST_DIR`")
+  endif()
+  bar()
+endfunction()
+
+foo()
+
+if(CMAKE_CURRENT_FUNCTION)
+  message(SEND_ERROR "`CMAKE_CURRENT_FUNCTION` is not expected to be set here")
+endif()
+if(CMAKE_CURRENT_FUNCTION_LIST_FILE)
+  message(SEND_ERROR "`CMAKE_CURRENT_FUNCTION_LIST_FILE` is not expected to be set here")
+endif()
+if(CMAKE_CURRENT_FUNCTION_LIST_DIR)
+  message(SEND_ERROR "`CMAKE_CURRENT_FUNCTION_LIST_DIR` is not expected to be set here")
+endif()
+if(CMAKE_CURRENT_FUNCTION_LIST_LINE)
+  message(SEND_ERROR "`CMAKE_CURRENT_FUNCTION_LIST_LINE` is not expected to be set here")
+endif()
+
+include("${CMAKE_CURRENT_LIST_DIR}/DummyMacro.cmake")
+
+function(calling_macro)
+  dummy()
+endfunction()
+
+calling_macro()
+
+cmake_policy(SET CMP0007 NEW)
+
+# ATTENTION `CMAKE_CURRENT_LIST_LINE` can't be used in `math()'
+function(print_self)
+    file(STRINGS "${CMAKE_CURRENT_FUNCTION_LIST_FILE}" _lines)
+    math(EXPR _begin "${CMAKE_CURRENT_FUNCTION_LIST_LINE} - 1")
+    list(SUBLIST _lines ${_begin} 7 _lines) # This function has 7 lines only
+    list(JOIN _lines "\n" _lines)
+    message(STATUS "Print the `${CMAKE_CURRENT_FUNCTION}` function:\n${_lines}")
+endfunction()
+
+print_self()

+ 3 - 0
Tests/RunCMake/function/CMakeLists.txt

@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 3.16)
+project(${RunCMake_TEST} NONE)
+include(${RunCMake_TEST}.cmake)

+ 20 - 0
Tests/RunCMake/function/DummyMacro.cmake

@@ -0,0 +1,20 @@
+macro(dummy)
+  if(NOT CMAKE_CURRENT_FUNCTION STREQUAL "calling_macro")
+    message(SEND_ERROR "Bad value of `CMAKE_CURRENT_FUNCTION`")
+  endif()
+  if(NOT CMAKE_CURRENT_FUNCTION_LIST_FILE MATCHES "^.*/function/CMAKE_CURRENT_FUNCTION.cmake$")
+    message(SEND_ERROR "Bad value of `CMAKE_CURRENT_FUNCTION_LIST_FILE`")
+  endif()
+  if(NOT CMAKE_CURRENT_FUNCTION_LIST_FILE STREQUAL _THIS_FILE)
+    message(SEND_ERROR "Bad value of `CMAKE_CURRENT_FUNCTION_LIST_FILE`")
+  endif()
+  if(NOT CMAKE_CURRENT_FUNCTION_LIST_DIR MATCHES "^.*/Tests/RunCMake/function$")
+    message(SEND_ERROR "Bad value of `CMAKE_CURRENT_FUNCTION_LIST_DIR`")
+  endif()
+  if(NOT CMAKE_CURRENT_FUNCTION_LIST_LINE EQUAL 77)
+    message(SEND_ERROR "Bad value of `CMAKE_CURRENT_FUNCTION_LIST_LINE`")
+  endif()
+  if(NOT CMAKE_CURRENT_FUNCTION_LIST_DIR STREQUAL _THIS_DIR)
+    message(SEND_ERROR "Bad value of `CMAKE_CURRENT_FUNCTION_LIST_DIR`")
+  endif()
+endmacro()

+ 3 - 0
Tests/RunCMake/function/RunCMakeTest.cmake

@@ -0,0 +1,3 @@
+include(RunCMake)
+
+run_cmake(CMAKE_CURRENT_FUNCTION)