diff --git a/docs/source/usage/project_integration.md b/docs/source/usage/project_integration.md index 19809a616175bee796a435b440951dffc07199b2..de3aa0c57558de101d2a1a06f07be11d3d937b0e 100644 --- a/docs/source/usage/project_integration.md +++ b/docs/source/usage/project_integration.md @@ -74,6 +74,7 @@ find_package( PystencilsSfg ) Make sure to set the `Python_ROOT_DIR` cache variable to point to the correct Python interpreter (i.e. the virtual environment you have installed *pystencils-sfg* into). +(cmake_add_generator_scripts)= ### Add generator scripts The primary interaction point in CMake is the function `pystencilssfg_generate_target_sources`, @@ -85,6 +86,7 @@ pystencilssfg_generate_target_sources( <target> [DEPENDS dependency1.py [dependency2.py...]] [FILE_EXTENSIONS <header-extension> <impl-extension>] [OUTPUT_MODE <standalone|inline|header-only>] + [CONFIG_MODULE <path-to-config-module.py>] ) ``` @@ -97,6 +99,9 @@ The function takes the following options: - `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`. + - `CONFIG_MODULE`: Set the configuration module for all scripts registered with this call. + If set, this overrides the value of `PystencilsSfg_CONFIG_MODULE` + in the current scope (see [](#cmake_set_config_module)) ### Include generated files @@ -110,9 +115,13 @@ path, such that generated header files for a target `<target>` may be included v (cmake_set_config_module)= ### Set a Configuration Module -To specify a [configuration module](#config_module) for your project, -set the scoped variable `PystencilsSfg_CONFIG_MODULE` to point at the respective Python file. -The pystencils-sfg CMake system will then pass that module to each generator script invocation. +There are two ways of specifying a [configuration module](#config_module) for generator scripts +registered with CMake: +- To set a configuration module for scripts registered with a single call to `pystencilssfg_generate_target_sources`, + use the `CONFIG_MODULE` function parameter (see [](#cmake_add_generator_scripts)). +- To set a config module for all generator scripts within the current CMake directory and its subdirectories, + set the scoped variable `PystencilsSfg_CONFIG_MODULE` to point at the respective Python file, e.g. + `set( PystencilsSfg_CONFIG_MODULE ProjectConfig.py )`. You might want to populate your configuration module with information about the current build setup and environment. diff --git a/mypy.ini b/mypy.ini index ca7990ba101438a4b0bebc14824640a2f0c29479..efef4eab417220d306836a87754293a3f9510aeb 100644 --- a/mypy.ini +++ b/mypy.ini @@ -3,3 +3,6 @@ python_version=3.10 [mypy-pystencils.*] ignore_missing_imports=true + +[mypy-sympy.*] +ignore_missing_imports=true diff --git a/pyproject.toml b/pyproject.toml index f942a18536ef30209cf48ecc320b4e625fd30a6c..b787da5c7180e32c6751bc8c78c2532f693382e2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,7 +9,7 @@ dependencies = [ ] requires-python = ">=3.10" readme = "README.md" -license = { file = "COPYING.txt" } +license = { file = "LICENSE" } dynamic = ["version"] [project.scripts] diff --git a/pytest.ini b/pytest.ini index 91afe80606639eb5ad33c77d899d7eb57b91d107..dd91f5271f8dd399e22854b40ec42abb7e0f6728 100644 --- a/pytest.ini +++ b/pytest.ini @@ -9,5 +9,6 @@ addopts = --ignore=tests/generator_scripts/deps --ignore=tests/generator_scripts/expected --ignore=tests/data + --ignore=tests/integration/cmake_project doctest_optionflags = NORMALIZE_WHITESPACE IGNORE_EXCEPTION_DETAIL diff --git a/src/pystencilssfg/cmake/modules/PystencilsSfg.cmake b/src/pystencilssfg/cmake/modules/PystencilsSfg.cmake index c42a251fda1c4201023e8150f9e35c4427e532c8..27659789625dc1df93bb59bde5af6260bb46e7e1 100644 --- a/src/pystencilssfg/cmake/modules/PystencilsSfg.cmake +++ b/src/pystencilssfg/cmake/modules/PystencilsSfg.cmake @@ -43,7 +43,7 @@ endfunction() function(pystencilssfg_generate_target_sources TARGET) set(options) - set(oneValueArgs OUTPUT_MODE) + set(oneValueArgs OUTPUT_MODE CONFIG_MODULE) set(multiValueArgs SCRIPTS DEPENDS FILE_EXTENSIONS) cmake_parse_arguments(_pssfg "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) @@ -53,21 +53,27 @@ function(pystencilssfg_generate_target_sources TARGET) list(APPEND generatorArgs "--sfg-output-mode=${_pssfg_OUTPUT_MODE}") endif() - if(DEFINED PystencilsSfg_CONFIGURATOR_SCRIPT) - message(AUTHOR_WARNING "The variable PystencilsSfg_CONFIGURATOR_SCRIPT is deprecated. Set PystencilsSfg_CONFIG_MODULE instead.") - cmake_path(ABSOLUTE_PATH PystencilsSfg_CONFIGURATOR_SCRIPT OUTPUT_VARIABLE configscript) - list(APPEND generatorArgs "--sfg-config-module=${configscript}") - list(APPEND _pssfg_DEPENDS ${configscript}) - endif() - - if(DEFINED PystencilsSfg_CONFIG_MODULE) + if(DEFINED _pssfg_CONFIG_MODULE) + cmake_path(ABSOLUTE_PATH _pssfg_CONFIG_MODULE OUTPUT_VARIABLE config_module) + list(APPEND generatorArgs "--sfg-config-module=${config_module}") + list(APPEND _pssfg_DEPENDS ${config_module}) + else() if(DEFINED PystencilsSfg_CONFIGURATOR_SCRIPT) - message(FATAL_ERROR "At most one of PystencilsSfg_CONFIGURATOR_SCRIPT and PystencilsSfg_CONFIG_MODULE may be set.") + message(AUTHOR_WARNING "The variable PystencilsSfg_CONFIGURATOR_SCRIPT is deprecated. Set PystencilsSfg_CONFIG_MODULE instead.") + cmake_path(ABSOLUTE_PATH PystencilsSfg_CONFIGURATOR_SCRIPT OUTPUT_VARIABLE configscript) + list(APPEND generatorArgs "--sfg-config-module=${configscript}") + list(APPEND _pssfg_DEPENDS ${configscript}) endif() - cmake_path(ABSOLUTE_PATH PystencilsSfg_CONFIG_MODULE OUTPUT_VARIABLE config_module) - list(APPEND generatorArgs "--sfg-config-module=${config_module}") - list(APPEND _pssfg_DEPENDS ${config_module}) + if(DEFINED PystencilsSfg_CONFIG_MODULE) + if(DEFINED PystencilsSfg_CONFIGURATOR_SCRIPT) + message(FATAL_ERROR "At most one of PystencilsSfg_CONFIGURATOR_SCRIPT and PystencilsSfg_CONFIG_MODULE may be set.") + endif() + + cmake_path(ABSOLUTE_PATH PystencilsSfg_CONFIG_MODULE OUTPUT_VARIABLE config_module) + list(APPEND generatorArgs "--sfg-config-module=${config_module}") + list(APPEND _pssfg_DEPENDS ${config_module}) + endif() endif() if(DEFINED _pssfg_FILE_EXTENSIONS) diff --git a/tests/integration/cmake_project/.gitignore b/tests/integration/cmake_project/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..2f4eb1ab954a587388367711592b5aa8d8f483b8 --- /dev/null +++ b/tests/integration/cmake_project/.gitignore @@ -0,0 +1 @@ +FindPystencilsSfg.cmake \ No newline at end of file diff --git a/tests/integration/cmake_project/CMakeLists.txt b/tests/integration/cmake_project/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..efee91238eec893bc523f5df4710b0d40125a2b2 --- /dev/null +++ b/tests/integration/cmake_project/CMakeLists.txt @@ -0,0 +1,28 @@ +cmake_minimum_required( VERSION 3.22 ) +project( sfg_cmake_project_test ) + +list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}) + +find_package( PystencilsSfg REQUIRED ) + +set( UseGlobalCfgModule OFF CACHE BOOL "Specify config module globally" ) +set( UseLocalCfgModule OFF CACHE BOOL "Specify config module locally" ) + +if( $CACHE{UseGlobalCfgModule} ) + set( PystencilsSfg_CONFIG_MODULE ${CMAKE_CURRENT_SOURCE_DIR}/gen_config.py ) +endif() + +add_executable( TestApp TestApp.cpp ) + +if( $CACHE{UseLocalCfgModule} ) + pystencilssfg_generate_target_sources( + TestApp + SCRIPTS GenTest.py + CONFIG_MODULE ${CMAKE_CURRENT_SOURCE_DIR}/gen_config.py + ) +else() + pystencilssfg_generate_target_sources( + TestApp + SCRIPTS GenTest.py + ) +endif() diff --git a/tests/integration/cmake_project/GenTest.py b/tests/integration/cmake_project/GenTest.py new file mode 100644 index 0000000000000000000000000000000000000000..8399e7061ae79b5b3a8c0fe6c3d544f0b5d6f586 --- /dev/null +++ b/tests/integration/cmake_project/GenTest.py @@ -0,0 +1,10 @@ +from pystencilssfg import SourceFileGenerator + +with SourceFileGenerator() as sfg: + sfg.namespace("gen") + + retval = 42 if sfg.context.project_info is None else sfg.context.project_info + + sfg.function("getValue", return_type="int")( + f"return {retval};" + ) diff --git a/tests/integration/cmake_project/TestApp.cpp b/tests/integration/cmake_project/TestApp.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7ceb98b42f40e6c91dddad76170b4654e14d4851 --- /dev/null +++ b/tests/integration/cmake_project/TestApp.cpp @@ -0,0 +1,5 @@ +#include "gen/TestApp/GenTest.hpp" + +int main(void) { + return int( gen::getValue() ); +} diff --git a/tests/integration/cmake_project/gen_config.py b/tests/integration/cmake_project/gen_config.py new file mode 100644 index 0000000000000000000000000000000000000000..3a62d2808b3356d0a9031a585a464e4f6f8151b5 --- /dev/null +++ b/tests/integration/cmake_project/gen_config.py @@ -0,0 +1,9 @@ +from pystencilssfg import SfgConfig + + +def configure_sfg(cfg: SfgConfig): + cfg.extensions.impl = "c++" + + +def project_info(): + return 31 diff --git a/tests/integration/test_cmake.py b/tests/integration/test_cmake.py new file mode 100644 index 0000000000000000000000000000000000000000..21091583114094e2639818ea6da01eb8db44b009 --- /dev/null +++ b/tests/integration/test_cmake.py @@ -0,0 +1,37 @@ +import pytest + +import pathlib +import subprocess + +THIS_DIR = pathlib.Path(__file__).parent + +CMAKE_PROJECT_DIRNAME = "cmake_project" +CMAKE_PROJECT_DIR = THIS_DIR / CMAKE_PROJECT_DIRNAME + + +@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"] + + result = subprocess.run(obtain_find_module_cmd, cwd=CMAKE_PROJECT_DIR) + assert result.returncode == 0 + + cmake_configure_cmd = ["cmake", "-S", CMAKE_PROJECT_DIR, "-B", str(tmp_path)] + if config_source is not None: + cmake_configure_cmd.append(f"-D{config_source}=ON") + configure_result = subprocess.run(cmake_configure_cmd) + assert configure_result.returncode == 0 + + cmake_build_cmd = ["cmake", "--build", str(tmp_path), "--target", "TestApp"] + build_result = subprocess.run(cmake_build_cmd) + assert build_result.returncode == 0 + + run_cmd = [str(tmp_path / "TestApp")] + run_result = subprocess.run(run_cmd) + + if config_source is not None: + assert (tmp_path / "sfg_sources" / "gen" / "TestApp" / "GenTest.c++").exists() + assert run_result.returncode == 31 + else: + assert (tmp_path / "sfg_sources" / "gen" / "TestApp" / "GenTest.cpp").exists() + assert run_result.returncode == 42