From 5cfdb2c1dff90a99638a6d4c050c69ef640f4394 Mon Sep 17 00:00:00 2001
From: Frederik Hennig <frederik.hennig@fau.de>
Date: Wed, 29 Jan 2025 21:09:22 +0100
Subject: [PATCH] enable passing custom cmdline options through cmake

---
 docs/source/usage/generator_scripts.md         |  1 +
 docs/source/usage/project_integration.md       |  2 ++
 .../cmake/modules/PystencilsSfg.cmake          | 18 ++++++++++++++----
 tests/integration/cmake_project/CMakeLists.txt |  7 +++++++
 tests/integration/cmake_project/CliTest.py     |  6 ++++++
 tests/integration/test_cmake.py                | 12 +++++++++++-
 6 files changed, 41 insertions(+), 5 deletions(-)
 create mode 100644 tests/integration/cmake_project/CliTest.py

diff --git a/docs/source/usage/generator_scripts.md b/docs/source/usage/generator_scripts.md
index d408602..4a1f6aa 100644
--- a/docs/source/usage/generator_scripts.md
+++ b/docs/source/usage/generator_scripts.md
@@ -320,6 +320,7 @@ with the `--help` flag:
 $ python kernels.py --help
 ```
 
+(custom_cli_args)=
 ## Adding Custom Command-Line Options
 
 Sometimes, you might want to add your own command-line options to a generator script
diff --git a/docs/source/usage/project_integration.md b/docs/source/usage/project_integration.md
index de3aa0c..8d3a826 100644
--- a/docs/source/usage/project_integration.md
+++ b/docs/source/usage/project_integration.md
@@ -83,6 +83,7 @@ with the following signature:
 ```CMake
 pystencilssfg_generate_target_sources( <target> 
     SCRIPTS script1.py [script2.py ...]
+    [ARGS arg1 [arg2 ...]]
     [DEPENDS dependency1.py [dependency2.py...]]
     [FILE_EXTENSIONS <header-extension> <impl-extension>]
     [OUTPUT_MODE <standalone|inline|header-only>]
@@ -96,6 +97,7 @@ Any changes in the generator scripts, or any listed dependency, will trigger reg
 The function takes the following options:
 
  - `SCRIPTS`: A list of generator scripts
+ - `ARGS`: A list of custom command line arguments passed to the generator scripts; see [](#custom_cli_args)
  - `DEPENDS`: A list of dependencies for the generator scripts
  - `FILE_EXTENSION`: The desired extensions for the generated files
  - `OUTPUT_MODE`: Sets the output mode of the code generator; see {any}`SfgConfig.output_mode`.
diff --git a/src/pystencilssfg/cmake/modules/PystencilsSfg.cmake b/src/pystencilssfg/cmake/modules/PystencilsSfg.cmake
index f8fe8dd..03b701d 100644
--- a/src/pystencilssfg/cmake/modules/PystencilsSfg.cmake
+++ b/src/pystencilssfg/cmake/modules/PystencilsSfg.cmake
@@ -19,7 +19,7 @@ file(MAKE_DIRECTORY "${PystencilsSfg_GENERATED_SOURCES_DIR}")
 function(_pssfg_add_gen_source target script)
     set(options)
     set(oneValueArgs)
-    set(multiValueArgs GENERATOR_ARGS DEPENDS)
+    set(multiValueArgs GENERATOR_ARGS USER_ARGS DEPENDS)
 
     cmake_parse_arguments(_pssfg "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
 
@@ -47,7 +47,7 @@ function(_pssfg_add_gen_source target script)
 
     add_custom_command(OUTPUT ${generatedSourcesAbsolute}
                        DEPENDS ${scriptAbsolute} ${_pssfg_DEPENDS}
-                       COMMAND ${PystencilsSfg_PYTHON_INTERPRETER} ${scriptAbsolute} ${_pssfg_GENERATOR_ARGS}
+                       COMMAND ${PystencilsSfg_PYTHON_INTERPRETER} ${scriptAbsolute} ${_pssfg_GENERATOR_ARGS} ${_pssfg_USER_ARGS}
                        WORKING_DIRECTORY "${generatedSourcesDir}")
 
     target_sources(${target} PRIVATE ${generatedSourcesAbsolute})
@@ -58,7 +58,7 @@ endfunction()
 function(pystencilssfg_generate_target_sources TARGET)
     set(options)
     set(oneValueArgs OUTPUT_MODE CONFIG_MODULE)
-    set(multiValueArgs SCRIPTS DEPENDS FILE_EXTENSIONS)
+    set(multiValueArgs SCRIPTS DEPENDS FILE_EXTENSIONS ARGS)
     cmake_parse_arguments(_pssfg "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
 
     set(generatorArgs)
@@ -96,8 +96,18 @@ function(pystencilssfg_generate_target_sources TARGET)
         list(APPEND generatorArgs "--sfg-file-extensions=${extensionsString}")
     endif()
 
+    if(DEFINED _pssfg_ARGS)
+        #   User has provided custom command line arguments
+        set(userArgs ${_pssfg_ARGS})
+    endif()
+
     foreach(codegenScript ${_pssfg_SCRIPTS})
-        _pssfg_add_gen_source(${TARGET} ${codegenScript} GENERATOR_ARGS ${generatorArgs} DEPENDS ${_pssfg_DEPENDS})
+        _pssfg_add_gen_source(
+            ${TARGET} ${codegenScript}
+            GENERATOR_ARGS ${generatorArgs}
+            USER_ARGS ${userArgs}
+            DEPENDS ${_pssfg_DEPENDS}
+        )
     endforeach()
     
 endfunction()
diff --git a/tests/integration/cmake_project/CMakeLists.txt b/tests/integration/cmake_project/CMakeLists.txt
index 8faa86d..e4470c9 100644
--- a/tests/integration/cmake_project/CMakeLists.txt
+++ b/tests/integration/cmake_project/CMakeLists.txt
@@ -30,3 +30,10 @@ else()
         SCRIPTS GenTest.py
     )
 endif()
+
+pystencilssfg_generate_target_sources(
+    TestApp
+    SCRIPTS CliTest.py
+    ARGS apples bananas unicorns
+    OUTPUT_MODE header-only
+)
diff --git a/tests/integration/cmake_project/CliTest.py b/tests/integration/cmake_project/CliTest.py
new file mode 100644
index 0000000..0456eb2
--- /dev/null
+++ b/tests/integration/cmake_project/CliTest.py
@@ -0,0 +1,6 @@
+from pystencilssfg import SourceFileGenerator
+
+with SourceFileGenerator(keep_unknown_argv=True) as sfg:
+    sfg.include("<string>")
+    for i, arg in enumerate(sfg.context.argv):
+        sfg.code(f"constexpr std::string arg{i} = \"{arg}\";")
diff --git a/tests/integration/test_cmake.py b/tests/integration/test_cmake.py
index 2109158..7d53964 100644
--- a/tests/integration/test_cmake.py
+++ b/tests/integration/test_cmake.py
@@ -9,7 +9,9 @@ CMAKE_PROJECT_DIRNAME = "cmake_project"
 CMAKE_PROJECT_DIR = THIS_DIR / CMAKE_PROJECT_DIRNAME
 
 
-@pytest.mark.parametrize("config_source", [None, "UseGlobalCfgModule", "UseLocalCfgModule"])
+@pytest.mark.parametrize(
+    "config_source", [None, "UseGlobalCfgModule", "UseLocalCfgModule"]
+)
 def test_cmake_project(tmp_path, config_source):
     obtain_find_module_cmd = ["sfg-cli", "cmake", "make-find-module"]
 
@@ -35,3 +37,11 @@ def test_cmake_project(tmp_path, config_source):
     else:
         assert (tmp_path / "sfg_sources" / "gen" / "TestApp" / "GenTest.cpp").exists()
         assert run_result.returncode == 42
+
+    cli_test_output = tmp_path / "sfg_sources" / "gen" / "TestApp" / "CliTest.hpp"
+    assert cli_test_output.exists()
+
+    content = cli_test_output.read_text()
+    assert 'arg0 = "apples";' in content
+    assert 'arg1 = "bananas";' in content
+    assert 'arg2 = "unicorns";' in content
-- 
GitLab