From ab978807fc18caabf81ba5ff1f02fcd07132dcbc Mon Sep 17 00:00:00 2001
From: Frederik Hennig <frederik.hennig@fau.de>
Date: Mon, 20 Nov 2023 14:23:24 +0900
Subject: [PATCH] extended cmake support via command-line interface

---
 src/pystencilssfg/cli.py                      | 50 ++++++++++++++-----
 .../cmake}/FindPystencilsSfg.cmake            |  6 +--
 src/pystencilssfg/cmake/__init__.py           | 11 +++-
 .../PystencilsSfg.cmake}                      |  2 +-
 tests/cmake_integration/.gitignore            |  1 +
 tests/cmake_integration/CMakeLists.txt        |  6 ++-
 6 files changed, 57 insertions(+), 19 deletions(-)
 rename {cmake => src/pystencilssfg/cmake}/FindPystencilsSfg.cmake (74%)
 rename src/pystencilssfg/cmake/{PystencilsSfgFunctions.cmake => modules/PystencilsSfg.cmake} (97%)
 create mode 100644 tests/cmake_integration/.gitignore

diff --git a/src/pystencilssfg/cli.py b/src/pystencilssfg/cli.py
index 458c1fe..a832642 100644
--- a/src/pystencilssfg/cli.py
+++ b/src/pystencilssfg/cli.py
@@ -1,7 +1,8 @@
 import sys
+import os
 from os import path
 
-from argparse import ArgumentParser
+from argparse import ArgumentParser, BooleanOptionalAction
 
 from .configuration import (
     SfgConfigException, SfgConfigSource,
@@ -9,25 +10,40 @@ from .configuration import (
 )
 
 
+def add_newline_arg(parser):
+    parser.add_argument("--newline", action=BooleanOptionalAction, default=True,
+                        help="Whether to add a terminating newline to the output.")
+
+
 def cli_main(program='sfg-cli'):
     parser = ArgumentParser(program,
-                            description="pystencilssfg command-line utility")
+                            description="pystencilssfg command-line utility for build system integration")
 
     subparsers = parser.add_subparsers(required=True, title="Subcommands")
 
-    version_parser = subparsers.add_parser(
-        "version", help="Print version and exit.")
+    version_parser = subparsers.add_parser("version", help="Print version and exit.")
+    add_newline_arg(version_parser)
     version_parser.set_defaults(func=version)
 
     outfiles_parser = subparsers.add_parser(
         "list-files", help="List files produced by given codegen script.")
 
     outfiles_parser.set_defaults(func=list_files)
-    outfiles_parser.add_argument(
-        "--format", type=str, choices=("human", "cmake"), default="human")
+    add_config_args_to_parser(outfiles_parser)
+    add_newline_arg(outfiles_parser)
+    outfiles_parser.add_argument("--sep", type=str, default=" ", dest="sep", help="Separator for list items")
     outfiles_parser.add_argument("codegen_script", type=str)
 
-    add_config_args_to_parser(outfiles_parser)
+    cmake_parser = subparsers.add_parser("cmake", help="Operations for CMake integation")
+    cmake_subparsers = cmake_parser.add_subparsers(required=True)
+
+    modpath = cmake_subparsers.add_parser("modulepath", help="Print the include path for the pystencils-sfg cmake module")
+    add_newline_arg(modpath)
+    modpath.set_defaults(func=print_cmake_modulepath)
+
+    findmod = cmake_subparsers.add_parser("make-find-module",
+                                          help="Creates the pystencils-sfg CMake find module as 'FindPystencilsSfg.cmake' in the current directory.")
+    findmod.set_defaults(func=make_cmake_find_module)
 
     args = parser.parse_args()
     args.func(args)
@@ -37,7 +53,9 @@ def cli_main(program='sfg-cli'):
 
 def version(args):
     from . import __version__
-    print(__version__)
+
+    print(__version__, end=os.linesep if args.newline else '')
+
     exit(0)
 
 
@@ -49,20 +67,28 @@ def list_files(args):
 
     config = merge_configurations(project_config, cmdline_config, None)
 
-    scriptdir, scriptname = path.split(args.codegen_script)
+    _, scriptname = path.split(args.codegen_script)
     basename = path.splitext(scriptname)[0]
 
     from .emitters.cpu.basic_cpu import BasicCpuEmitter
 
     emitter = BasicCpuEmitter(basename, config)
 
-    match args.format:
-        case "human": print(" ".join(emitter.output_files))
-        case "cmake": print(";".join(emitter.output_files), end="")
+    print(args.sep.join(emitter.output_files), end=os.linesep if args.newline else '')
 
     exit(0)
 
 
+def print_cmake_modulepath(args):
+    from .cmake import get_sfg_cmake_modulepath
+    print(get_sfg_cmake_modulepath(), end=os.linesep if args.newline else '')
+
+
+def make_cmake_find_module(args):
+    from .cmake import make_find_module
+    make_find_module()
+
+
 def abort_with_config_exception(exception: SfgConfigException):
     def eprint(*args, **kwargs):
         print(*args, file=sys.stderr, **kwargs)
diff --git a/cmake/FindPystencilsSfg.cmake b/src/pystencilssfg/cmake/FindPystencilsSfg.cmake
similarity index 74%
rename from cmake/FindPystencilsSfg.cmake
rename to src/pystencilssfg/cmake/FindPystencilsSfg.cmake
index bdc5132..c1affee 100644
--- a/cmake/FindPystencilsSfg.cmake
+++ b/src/pystencilssfg/cmake/FindPystencilsSfg.cmake
@@ -6,7 +6,7 @@ find_package( Python COMPONENTS Interpreter REQUIRED )
 
 #   Try to find pystencils-sfg in the python environment
 
-execute_process(COMMAND ${Python_EXECUTABLE} -c "import pystencilssfg; print(pystencilssfg.__version__, end='')"
+execute_process(COMMAND ${Python_EXECUTABLE} -m pystencilssfg version --no-newline
                 RESULT_VARIABLE _PystencilsSfgFindResult OUTPUT_VARIABLE PystencilsSfg_VERSION )
 
 if(${_PystencilsSfgFindResult} EQUAL 0)
@@ -20,10 +20,10 @@ endif()
 if(${PystencilsSfg_FOUND})
     message( STATUS "Found pystencils Source File Generator (Version ${PystencilsSfg_VERSION})")
     
-    execute_process(COMMAND ${Python_EXECUTABLE} -c "from pystencilssfg.cmake import get_sfg_cmake_modulepath; print(get_sfg_cmake_modulepath(), end='')"
+    execute_process(COMMAND ${Python_EXECUTABLE} -m pystencilssfg cmake modulepath --no-newline
                     OUTPUT_VARIABLE _PystencilsSfg_CMAKE_MODULE_PATH)
 
     set( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${_PystencilsSfg_CMAKE_MODULE_PATH})
-    include( PystencilsSfgFunctions )
+    include( PystencilsSfg )
 endif()
 
diff --git a/src/pystencilssfg/cmake/__init__.py b/src/pystencilssfg/cmake/__init__.py
index 7e08187..39b3cb6 100644
--- a/src/pystencilssfg/cmake/__init__.py
+++ b/src/pystencilssfg/cmake/__init__.py
@@ -1,5 +1,12 @@
-from os.path import dirname, realpath
+from os.path import dirname, realpath, join
+import shutil
 
 
 def get_sfg_cmake_modulepath():
-    return dirname(realpath(__file__))
+    return join(dirname(realpath(__file__)), "modules")
+
+
+def make_find_module():
+    cmake_dir = dirname(realpath(__file__))
+    find_module_file = join(cmake_dir, "FindPystencilsSfg.cmake")
+    shutil.copy(find_module_file, "FindPystencilsSfg.cmake")
diff --git a/src/pystencilssfg/cmake/PystencilsSfgFunctions.cmake b/src/pystencilssfg/cmake/modules/PystencilsSfg.cmake
similarity index 97%
rename from src/pystencilssfg/cmake/PystencilsSfgFunctions.cmake
rename to src/pystencilssfg/cmake/modules/PystencilsSfg.cmake
index 3af2a29..b2d98f2 100644
--- a/src/pystencilssfg/cmake/PystencilsSfgFunctions.cmake
+++ b/src/pystencilssfg/cmake/modules/PystencilsSfg.cmake
@@ -19,7 +19,7 @@ function(_pssfg_add_gen_source target script)
     get_filename_component(basename ${script} NAME_WLE)
     cmake_path(ABSOLUTE_PATH script OUTPUT_VARIABLE scriptAbsolute)
 
-    execute_process(COMMAND ${Python_EXECUTABLE} -m pystencilssfg list-files --format=cmake ${_pssfg_GENERATOR_ARGS} ${script}
+    execute_process(COMMAND ${Python_EXECUTABLE} -m pystencilssfg list-files "--sep=\;" --no-newline ${_pssfg_GENERATOR_ARGS} ${script}
                     OUTPUT_VARIABLE generatedSources RESULT_VARIABLE _pssfg_result
                     ERROR_VARIABLE _pssfg_stderr)
 
diff --git a/tests/cmake_integration/.gitignore b/tests/cmake_integration/.gitignore
new file mode 100644
index 0000000..facd9a1
--- /dev/null
+++ b/tests/cmake_integration/.gitignore
@@ -0,0 +1 @@
+.cmake
\ No newline at end of file
diff --git a/tests/cmake_integration/CMakeLists.txt b/tests/cmake_integration/CMakeLists.txt
index d107541..54769ad 100644
--- a/tests/cmake_integration/CMakeLists.txt
+++ b/tests/cmake_integration/CMakeLists.txt
@@ -2,7 +2,11 @@ cmake_minimum_required( VERSION 3.24 )
 
 project( pssfg_cmake_integration_test )
 
-set( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${pssfg_cmake_integration_test_SOURCE_DIR}/../../cmake )
+set( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${pssfg_cmake_integration_test_SOURCE_DIR}/.cmake )
+
+#   Don't try this at home!
+execute_process( COMMAND sfg-cli cmake make-find-module
+                 WORKING_DIRECTORY ${pssfg_cmake_integration_test_SOURCE_DIR}/.cmake )
 
 find_package( PystencilsSfg REQUIRED )
 
-- 
GitLab