diff --git a/.gitignore b/.gitignore index 0788a35dd6396699d33367a62f94f0b47bbf64c6..0afe9f97ae501e7ffcbc76ee101684b62305617b 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ **/bin **/obj **/out -**/generated_src \ No newline at end of file +**/generated_src +**/build \ No newline at end of file diff --git a/Notes.md b/Notes.md new file mode 100644 index 0000000000000000000000000000000000000000..1c8539b0c284f49f2b9db2e419acd6a57800f99e --- /dev/null +++ b/Notes.md @@ -0,0 +1,27 @@ + +# Build System Integration + +## Configurator Script + +The configurator script should configure the code generator and provide global configuration to all codegen scripts. +In the CMake integration, it can be specified globally via the `PystencilsSfg_CONFIGURATOR_SCRIPT` cache variable. + +To decide and implement: + + - Use `runpy` and communicate via a global variable, or use `importlib.util.spec_from_file_location` and communicate via + a function call? In either case, there needs to be concensus about at least one name in the configurator script. + - Allow specifying a separate configurator file at `pystencilssfg_generate_target_sources`? Sound sensible... It's basically + for free with the potential to add lots of flexibility + +## Generator flags + +Two separate lists of flags may be passed to generator scripts: Some may be evaluated by the SFG, and the rest +will be passed on to the user script. + +Arguments to the SFG include: + + - Path of the configurator script + - Output directory + +How to separate user from generator arguments? + diff --git a/cmake/FindPystencilsSfg.cmake b/cmake/FindPystencilsSfg.cmake new file mode 100644 index 0000000000000000000000000000000000000000..27eb1c01bdb06a7cf730d229f423c64f0dc68a6d --- /dev/null +++ b/cmake/FindPystencilsSfg.cmake @@ -0,0 +1,30 @@ +set( PystencilsSfg_FOUND OFF CACHE BOOL "pystencils source file generator found" ) +set( PystencilsSfg_CONFIGURATOR_SCRIPT "" CACHE STRING "Configurator script for the pystencils source file generator" ) + +mark_as_advanced( PystencilsSfg_FOUND ) + +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='')" + RESULT_VARIABLE _PystencilsSfgFindResult OUTPUT_VARIABLE PystencilsSfg_VERSION ) + +if(${_PystencilsSfgFindResult} EQUAL 0) + set( PystencilsSfg_FOUND ON ) +endif() + +if(${PystencilsSfg_FIND_REQUIRED} AND (NOT ${PystencilsSfg_FOUND})) + message( FATAL_ERROR "Could not find pystencils-sfg in current Python environment." ) +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='')" + OUTPUT_VARIABLE _PystencilsSfg_CMAKE_MODULE_PATH) + + set( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${_PystencilsSfg_CMAKE_MODULE_PATH}) + include( PystencilsSfgFunctions ) +endif() + diff --git a/pystencilssfg/__init__.py b/pystencilssfg/__init__.py index a56913f1d6afb919b8456f5d5d6a2e552dcedcb3..100f11f4eab2d880f87c2e43cccea3615b492269 100644 --- a/pystencilssfg/__init__.py +++ b/pystencilssfg/__init__.py @@ -7,3 +7,5 @@ __all__ = [ SourceFileGenerator, SfgContext, SfgKernelNamespace, SfgKernelHandle, PsType, SrcType ] + +__version__ = "0.0.0" diff --git a/pystencilssfg/cmake/PystencilsSfgFunctions.cmake b/pystencilssfg/cmake/PystencilsSfgFunctions.cmake new file mode 100644 index 0000000000000000000000000000000000000000..4dcefa9af148eb2b3e5d3828c7b44a69c22de58b --- /dev/null +++ b/pystencilssfg/cmake/PystencilsSfgFunctions.cmake @@ -0,0 +1,33 @@ + +set(PSSFG_GENERATED_SOURCES_DIR "${CMAKE_BINARY_DIR}/pystencils_generated_sources") +file(MAKE_DIRECTORY "${PSSFG_GENERATED_SOURCES_DIR}") +include_directories(${PSSFG_GENERATED_SOURCES_DIR}) + +function(pystencilssfg_generate_target_sources) + set(options) + set(oneValueArgs TARGET SCRIPT) + set(multiValueArgs DEPENDS) + cmake_parse_arguments(GENSRC "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + set(generatedSourcesDir ${PSSFG_GENERATED_SOURCES_DIR}/gen/${GENSRC_TARGET}) + + get_filename_component(basename ${GENSRC_SCRIPT} NAME_WLE) + cmake_path(ABSOLUTE_PATH GENSRC_SCRIPT OUTPUT_VARIABLE pythonFile) + + set(generatedSourceFiles ${basename}.h ${basename}.cpp) + set(generatedWithAbsolutePath) + foreach (filename ${generatedSourceFiles}) + list(APPEND generatedWithAbsolutePath ${generatedSourcesDir}/${filename}) + endforeach () + + file(MAKE_DIRECTORY "${generatedSourcesDir}") + + # TODO: Get generator arguments via PYSTENCILS_GENERATOR_FLAGS, source file and target properties + + add_custom_command(OUTPUT ${generatedWithAbsolutePath} + DEPENDS ${pythonFile} ${GENSRC_DEPENDS} + COMMAND ${Python_EXECUTABLE} ${pythonFile} + WORKING_DIRECTORY "${generatedSourcesDir}") + + target_sources(${GENSRC_TARGET} PRIVATE ${generatedWithAbsolutePath}) +endfunction() diff --git a/pystencilssfg/cmake/__init__.py b/pystencilssfg/cmake/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..7e081872dc7fb71d0e00dacf6ff936a1d177a714 --- /dev/null +++ b/pystencilssfg/cmake/__init__.py @@ -0,0 +1,5 @@ +from os.path import dirname, realpath + + +def get_sfg_cmake_modulepath(): + return dirname(realpath(__file__)) diff --git a/pystencilssfg/context.py b/pystencilssfg/context.py index c4eb5952a3dc3e30852ecdaf88a17d9d1a707c9c..f2d968d9252c9ad63d7a96a03aedfdc337a06a34 100644 --- a/pystencilssfg/context.py +++ b/pystencilssfg/context.py @@ -36,22 +36,22 @@ class SourceFileGenerator: parser = ArgumentParser( "pystencilssfg", - description="pystencils Source File Generator") + description="pystencils Source File Generator", + allow_abbrev=False) - parser.add_argument("script_args", nargs='*') - parser.add_argument("-d", "--output-dir", type=str, default='.', dest='output_directory') + parser.add_argument("-d", "--sfg-output-dir", type=str, default='.', dest='output_directory') - args = parser.parse_args(sys.argv) + generator_args, script_args = parser.parse_known_args(sys.argv) import __main__ scriptpath = __main__.__file__ scriptname = path.split(scriptpath)[1] basename = path.splitext(scriptname)[0] - self._context = SfgContext(args.script_args, namespace, codestyle) + self._context = SfgContext(script_args, namespace, codestyle) from .emitters.cpu.basic_cpu import BasicCpuEmitter - self._emitter = BasicCpuEmitter(self._context, basename, args.output_directory) + self._emitter = BasicCpuEmitter(self._context, basename, generator_args.output_directory) def clean_files(self): for file in self._emitter.output_files: diff --git a/tests/cmake_integration/CMakeLists.txt b/tests/cmake_integration/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..f67ed7b857c69cb78636c8ec4ccb80a884c55e0f --- /dev/null +++ b/tests/cmake_integration/CMakeLists.txt @@ -0,0 +1,10 @@ +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 ) + +find_package( PystencilsSfg REQUIRED ) + +add_library( genlib ) +pystencilssfg_generate_target_sources( TARGET genlib SCRIPT kernels.py ) diff --git a/tests/cmake_integration/kernels.py b/tests/cmake_integration/kernels.py new file mode 100644 index 0000000000000000000000000000000000000000..7a679447fd9883a3b3593c67ca293afd6f9bab99 --- /dev/null +++ b/tests/cmake_integration/kernels.py @@ -0,0 +1,23 @@ +import sympy as sp +import numpy as np + +from pystencils.session import * + +from pystencilssfg import SourceFileGenerator +from pystencilssfg.source_concepts.cpp import std_mdspan + + +with SourceFileGenerator("poisson") as sfg: + src, dst = ps.fields("src, dst(1) : double[2D]") + + h = sp.Symbol('h') + + @ps.kernel + def poisson_jacobi(): + dst[0,0] @= (src[1, 0] + src[-1, 0] + src[0, 1] + src[0, -1]) / 4 + + poisson_kernel = sfg.kernels.create(poisson_jacobi) + + sfg.function("jacobi_smooth")( + sfg.call(poisson_kernel) + )