From d37460fa3c9bb37ec51ceb0338fa4c7da2ed699a Mon Sep 17 00:00:00 2001
From: Sergey Kosukhin <sergey.kosukhin@mpimet.mpg.de>
Date: Wed, 18 Dec 2024 10:51:07 +0100
Subject: [PATCH] cmake: build Python interface

---
 .cmake-format.py              |  9 +++++++++
 CMakeLists.txt                |  8 ++++++++
 cmake/AutoconfConfigure.cmake | 32 +++++++++++++++++++++++++++++++
 python/CMakeLists.txt         | 36 +++++++++++++++++++++++++++++++++++
 src/CMakeLists.txt            |  2 +-
 5 files changed, 86 insertions(+), 1 deletion(-)
 create mode 100644 cmake/AutoconfConfigure.cmake
 create mode 100644 python/CMakeLists.txt

diff --git a/.cmake-format.py b/.cmake-format.py
index 35a3748a..f89c292a 100644
--- a/.cmake-format.py
+++ b/.cmake-format.py
@@ -5,6 +5,15 @@
 
 with section("parse"):  # noqa: F821
     additional_commands = {
+        "autoconf_configure": {
+            "pargs": 0,
+            "kwargs": {
+                "INPUTS": "+",
+                "OUTPUTS": "+",
+                "VARIABLES": "*",
+                "VALUES": "*",
+            },
+        },
         "search_function_library": {
             "pargs": 3,
             "kwargs": {"OPTIONS": "*"},
diff --git a/CMakeLists.txt b/CMakeLists.txt
index b7a88ca0..cd12dcd7 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -15,6 +15,7 @@ option(BUILD_SHARED_LIBS "Build shared libraries" ON)
 option(BUILD_TESTING "Build tests" ON)
 
 option(MTIME_ENABLE_FORTRAN_HL "Enable the high-level Fortran interface" ON)
+option(MTIME_ENABLE_PYTHON "Enable the Python interface" OFF)
 option(MTIME_BUILD_EXAMPLES "Build examples" ON)
 option(MTIME_BUILD_DOCS "Build documentation" OFF)
 
@@ -30,6 +31,13 @@ include(GNUInstallDirs)
 
 add_subdirectory(src)
 
+if(MTIME_ENABLE_PYTHON)
+  # Technically, we can produce the Python interface without the interpreter but
+  # we REQUIRE it for now for the consistency with the Autotools-based building:
+  find_package(Python REQUIRED COMPONENTS Interpreter)
+  add_subdirectory(python)
+endif()
+
 if(MTIME_BUILD_DOCS)
   add_subdirectory(doc)
 endif()
diff --git a/cmake/AutoconfConfigure.cmake b/cmake/AutoconfConfigure.cmake
new file mode 100644
index 00000000..71514df5
--- /dev/null
+++ b/cmake/AutoconfConfigure.cmake
@@ -0,0 +1,32 @@
+# Copyright (c) 2013-2024 MPI-M, Luis Kornblueh, Rahul Sinha and DWD, Florian Prill. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+# ~~~
+# autoconf_configure(INPUTS <inputs>,
+#                    OUTPUTS <outputs>,
+#                    VARIABLES <variables>,
+#                    VALUES <values>)
+# ~~~
+# Iterates over the Autoconf templates from the <inputs> list, replaces the
+# variable references from the <variables> list with the <values> (only the
+# Autoconf-like @VAR@ forms) and stores the result to the respective files from
+# the <outputs> list. The <inputs> and the <outputs> must have the same length.
+# The <variables> and the <values> must have the same length. The function helps
+# in keeping the scope of the main script free from variables needed for
+# substitution only.
+#
+function(autoconf_configure)
+  cmake_parse_arguments(
+    PARSE_ARGV 0 ARG "" "" "INPUTS;OUTPUTS;VARIABLES;VALUES"
+  )
+
+  foreach(variable value IN ZIP_LISTS ARG_VARIABLES ARG_VALUES)
+    set(${variable} "${value}")
+  endforeach()
+
+  foreach(input output IN ZIP_LISTS ARG_INPUTS ARG_OUTPUTS)
+    configure_file(${input} ${output} @ONLY)
+  endforeach()
+endfunction()
diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt
new file mode 100644
index 00000000..26b57ce3
--- /dev/null
+++ b/python/CMakeLists.txt
@@ -0,0 +1,36 @@
+# Copyright (c) 2013-2024 MPI-M, Luis Kornblueh, Rahul Sinha and DWD, Florian Prill. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+set(module_dir_name mtime)
+set(module_source_dir ${CMAKE_CURRENT_SOURCE_DIR}/${module_dir_name})
+set(module_binary_dir ${CMAKE_CURRENT_BINARY_DIR}/${module_dir_name})
+
+add_library(mtime_py MODULE)
+
+set_target_properties(
+  mtime_py
+  PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${module_binary_dir}
+             OUTPUT_NAME __mtime
+             PREFIX ""
+             SUFFIX "${CMAKE_SHARED_MODULE_SUFFIX}"
+)
+
+target_link_libraries(mtime_py PRIVATE mtime_c)
+
+include(AutoconfConfigure)
+# Also create ${module_binary_dir}:
+autoconf_configure(
+  INPUTS ${module_source_dir}/_mtime.py.in
+  OUTPUTS ${module_binary_dir}/_mtime.py
+  VARIABLES acx_modext
+  VALUES "${CMAKE_SHARED_MODULE_SUFFIX}"
+)
+
+file(
+  CREATE_LINK ${module_source_dir}/__init__.py ${module_binary_dir}/__init__.py
+  RESULT
+    dummy # just don't fail if something goes wrong (e.g. building in-source)
+  COPY_ON_ERROR SYMBOLIC
+)
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 9ff64d15..cf0cb9e0 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -54,7 +54,7 @@ set_target_properties(
              EXPORT_NAME mtime::mtime_c
 )
 
-if(BUILD_SHARED_LIBS)
+if(BUILD_SHARED_LIBS OR MTIME_ENABLE_PYTHON)
   set_target_properties(mtime_c PROPERTIES POSITION_INDEPENDENT_CODE ON)
 endif()
 
-- 
GitLab