Просмотр исходного кода

cmFunctionCommand: Introduce `CMAKE_CURRENT_FUNCTION*` variables

`CMAKE_CURRENT_FUNCTION`
  Can be used for diagnostic or debugging messages like the
  `__PRETTY_FUNCTION__` macro of GCC.

`CMAKE_CURRENT_FUNCTION_LIST_DIR`
  Eliminates the necessity of the additional "global"
  variables inside a module used to access additional "resource"
  files from functions defined in the module.

...
Alex Turbov 6 лет назад
Родитель
Сommit
90e3e2a777

+ 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
 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

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

@@ -37,6 +37,10 @@ Variables that Provide Information
    /variable/CMAKE_CROSSCOMPILING_EMULATOR
    /variable/CMAKE_CTEST_COMMAND
    /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_FILE
    /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.

+ 21 - 0
Source/cmFunctionCommand.cxx

@@ -18,11 +18,19 @@
 #include "cmRange.h"
 #include "cmState.h"
 #include "cmStringAlgorithms.h"
+#include "cmSystemTools.h"
 
 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
 class cmFunctionHelperCommand
@@ -39,6 +47,7 @@ public:
   std::vector<cmListFileFunction> Functions;
   cmPolicies::PolicyMap Policies;
   std::string FilePath;
+  long Line;
 };
 
 bool cmFunctionHelperCommand::operator()(
@@ -89,6 +98,17 @@ bool cmFunctionHelperCommand::operator()(
   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.
   // for each function
   for (cmListFileFunction const& func : this->Functions) {
@@ -143,6 +163,7 @@ bool cmFunctionFunctionBlocker::Replay(
   f.Args = this->Args;
   f.Functions = std::move(functions);
   f.FilePath = this->GetStartingContext().FilePath;
+  f.Line = this->GetStartingContext().Line;
   mf.RecordPolicies(f.Policies);
   mf.GetState()->AddScriptedCommand(this->Args.front(), std::move(f));
   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_program -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME})
 add_RunCMake_test(foreach)
+add_RunCMake_test(function)
 add_RunCMake_test(get_filename_component)
 add_RunCMake_test(get_property)
 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)