From f30f0fa47180c7a7d99cc9e88643dcdcb5af5a1a Mon Sep 17 00:00:00 2001
From: Dominik Thoennes <dominik.thoennes@fau.de>
Date: Mon, 18 May 2020 16:16:16 +0200
Subject: [PATCH] Replaced `waLBerla_python_file_generates` with
 `waLBerla_generate_target_from_python`

This new function does the actual generation using pystencils and creates a target library that can be used as a dependency as usual. One should not list the python script itself anymore in add_executable.
Convention: Use the AppName as a prefix for the generated lib!

Example Usage:

waLBerla_generate_target_from_python( NAME AppNameGeneratedLib FILE GenerateKernel.py OUT_FILES Kernel.h Kernel.cpp)

waLBerla_add_executable ( NAME AppNameGeneratedLibTest DEPENDS GeneratedLib)
---
 apps/benchmarks/CMakeLists.txt                |   2 +-
 .../FluidParticleCoupling/CMakeLists.txt      |  44 ++++---
 apps/benchmarks/UniformGridGPU/CMakeLists.txt |  82 +++++++------
 .../UniformGridGenerated/CMakeLists.txt       |  24 ++--
 cmake/waLBerlaFunctions.cmake                 |  76 +-----------
 cmake/waLBerlaHelperFunctions.cmake           | 114 +++++++++---------
 .../pystencils_walberla/cmake_integration.py  |   2 +-
 tests/cuda/CMakeLists.txt                     |  18 +--
 tests/field/CMakeLists.txt                    |   5 +-
 tests/lbm/CMakeLists.txt                      |  19 ++-
 10 files changed, 169 insertions(+), 217 deletions(-)

diff --git a/apps/benchmarks/CMakeLists.txt b/apps/benchmarks/CMakeLists.txt
index 66e99fb27..3037383db 100644
--- a/apps/benchmarks/CMakeLists.txt
+++ b/apps/benchmarks/CMakeLists.txt
@@ -14,5 +14,5 @@ add_subdirectory( PoiseuilleChannel )
 add_subdirectory( ProbeVsExtraMessage )
 add_subdirectory( SchaeferTurek )
 add_subdirectory( UniformGrid )
-add_subdirectory( UniformGridGPU )
 add_subdirectory( UniformGridGenerated )
+add_subdirectory( UniformGridGPU )
diff --git a/apps/benchmarks/FluidParticleCoupling/CMakeLists.txt b/apps/benchmarks/FluidParticleCoupling/CMakeLists.txt
index c42c1e497..e55a56959 100644
--- a/apps/benchmarks/FluidParticleCoupling/CMakeLists.txt
+++ b/apps/benchmarks/FluidParticleCoupling/CMakeLists.txt
@@ -1,36 +1,42 @@
 waLBerla_link_files_to_builddir( "*.dat" )
 
-waLBerla_python_file_generates(GeneratedLBM.py
-      GeneratedLBM.cpp GeneratedLBM.h
+waLBerla_generate_target_from_python(NAME FluidParticleCouplingGeneratedLBM FILE GeneratedLBM.py
+      OUT_FILES GeneratedLBM.cpp GeneratedLBM.h
       )
 
-waLBerla_python_file_generates(GeneratedLBMWithForce.py
-      GeneratedLBMWithForce.cpp GeneratedLBMWithForce.h
+waLBerla_generate_target_from_python(NAME FluidParticleCouplingGeneratedLBMWithForce FILE GeneratedLBMWithForce.py
+      OUT_FILES GeneratedLBMWithForce.cpp GeneratedLBMWithForce.h
       )
 
-
 if( WALBERLA_BUILD_WITH_CODEGEN )
 
-waLBerla_add_executable ( NAME SphereWallCollision FILES SphereWallCollision.cpp GeneratedLBM.py
-                          DEPENDS blockforest boundary core domain_decomposition field lbm lbm_mesapd_coupling mesa_pd postprocessing timeloop vtk )
+   waLBerla_add_executable(NAME SphereWallCollision FILES SphereWallCollision.cpp
+         DEPENDS blockforest boundary core domain_decomposition field lbm lbm_mesapd_coupling
+         mesa_pd postprocessing timeloop vtk FluidParticleCouplingGeneratedLBM)
 
-waLBerla_add_executable ( NAME SettlingSphereInBox FILES SettlingSphereInBox.cpp GeneratedLBM.py
-                          DEPENDS blockforest boundary core domain_decomposition field lbm lbm_mesapd_coupling mesa_pd postprocessing timeloop vtk )
+   waLBerla_add_executable(NAME SettlingSphereInBox FILES SettlingSphereInBox.cpp
+         DEPENDS blockforest boundary core domain_decomposition field lbm lbm_mesapd_coupling
+         mesa_pd postprocessing timeloop vtk FluidParticleCouplingGeneratedLBM)
 
-waLBerla_add_executable ( NAME SphereMovingWithPrescribedVelocity FILES SphereMovingWithPrescribedVelocity.cpp GeneratedLBM.py
-                          DEPENDS blockforest boundary core domain_decomposition field lbm mesa_pd lbm_mesapd_coupling postprocessing timeloop vtk )
+   waLBerla_add_executable(NAME SphereMovingWithPrescribedVelocity FILES SphereMovingWithPrescribedVelocity.cpp
+         DEPENDS blockforest boundary core domain_decomposition field lbm mesa_pd lbm_mesapd_coupling
+         postprocessing timeloop vtk FluidParticleCouplingGeneratedLBM)
 
-waLBerla_add_executable ( NAME LubricationForceEvaluation FILES LubricationForceEvaluation.cpp GeneratedLBM.py
-                          DEPENDS blockforest boundary core domain_decomposition field lbm lbm_mesapd_coupling mesa_pd postprocessing timeloop vtk )
+   waLBerla_add_executable(NAME LubricationForceEvaluation FILES LubricationForceEvaluation.cpp
+         DEPENDS blockforest boundary core domain_decomposition field lbm lbm_mesapd_coupling mesa_pd
+         postprocessing timeloop vtk FluidParticleCouplingGeneratedLBM)
 
-waLBerla_add_executable ( NAME DragForceSphere FILES DragForceSphere.cpp GeneratedLBMWithForce.py
-                          DEPENDS blockforest boundary core domain_decomposition field lbm lbm_mesapd_coupling mesa_pd postprocessing timeloop vtk )
+   waLBerla_add_executable(NAME DragForceSphere FILES DragForceSphere.cpp
+         DEPENDS blockforest boundary core domain_decomposition field lbm lbm_mesapd_coupling mesa_pd
+         postprocessing timeloop vtk FluidParticleCouplingGeneratedLBMWithForce)
 
-waLBerla_add_executable ( NAME ForcesOnSphereNearPlane FILES ForcesOnSphereNearPlane.cpp GeneratedLBM.py
-                          DEPENDS blockforest boundary core domain_decomposition field lbm lbm_mesapd_coupling mesa_pd postprocessing timeloop vtk )
+   waLBerla_add_executable(NAME ForcesOnSphereNearPlane FILES ForcesOnSphereNearPlane.cpp
+         DEPENDS blockforest boundary core domain_decomposition field lbm lbm_mesapd_coupling mesa_pd
+         postprocessing timeloop vtk FluidParticleCouplingGeneratedLBM)
 
-waLBerla_add_executable ( NAME ObliqueWetCollision FILES ObliqueWetCollision.cpp GeneratedLBM.py
-                          DEPENDS blockforest boundary core domain_decomposition field lbm lbm_mesapd_coupling mesa_pd postprocessing timeloop vtk )
+   waLBerla_add_executable(NAME ObliqueWetCollision FILES ObliqueWetCollision.cpp
+         DEPENDS blockforest boundary core domain_decomposition field lbm lbm_mesapd_coupling mesa_pd
+         postprocessing timeloop vtk FluidParticleCouplingGeneratedLBM)
 
 else()
 
diff --git a/apps/benchmarks/UniformGridGPU/CMakeLists.txt b/apps/benchmarks/UniformGridGPU/CMakeLists.txt
index 886cbf456..9ab6d1e78 100644
--- a/apps/benchmarks/UniformGridGPU/CMakeLists.txt
+++ b/apps/benchmarks/UniformGridGPU/CMakeLists.txt
@@ -2,43 +2,45 @@
 waLBerla_link_files_to_builddir( "*.prm" )
 #waLBerla_link_files_to_builddir( "simulation_setup" )
 
-waLBerla_python_file_generates(UniformGridGPU.py
-        UniformGridGPU_LatticeModel.cpp UniformGridGPU_LatticeModel.h
-        UniformGridGPU_LbKernel.cu UniformGridGPU_LbKernel.h
-        UniformGridGPU_NoSlip.cu UniformGridGPU_NoSlip.h
-        UniformGridGPU_UBB.cu UniformGridGPU_UBB.h
-        UniformGridGPU_PackInfo.cu UniformGridGPU_PackInfo.h
-        UniformGridGPU_MacroSetter.cpp UniformGridGPU_MacroSetter.h
-        UniformGridGPU_MacroGetter.cpp UniformGridGPU_MacroGetter.h
-        UniformGridGPU_Defines.h
-        )
-
-foreach(config srt trt mrt smagorinsky entropic smagorinsky_noopt entropic_kbc_n4
-        entropic_kbc_n4_noopt mrt_noopt mrt_full mrt_full_noopt
-        cumulant cumulant_d3q27
-        srt_d3q27 mrt_d3q27 mrt_d3q27_noopt smagorinsky_d3q27 smagorinsky_d3q27_noopt mrt_full_d3q27 mrt_full_d3q27_noopt)
-    waLBerla_add_executable ( NAME UniformGridBenchmarkGPU_${config}
-                              FILES UniformGridGPU.cpp UniformGridGPU.py
-                              DEPENDS blockforest boundary core cuda domain_decomposition field geometry timeloop vtk gui
-                              CODEGEN_CFG ${config})
-endforeach()
-
-
-
-
-waLBerla_python_file_generates(UniformGridGPU_AA.py
-        UniformGridGPU_AA_LbKernelEven.cu UniformGridGPU_AA_LbKernelEven.h
-        UniformGridGPU_AA_LbKernelOdd.cu  UniformGridGPU_AA_LbKernelOdd.h
-        UniformGridGPU_AA_PackInfoPull.cu UniformGridGPU_AA_PackInfoPull.h
-        UniformGridGPU_AA_PackInfoPush.cu UniformGridGPU_AA_PackInfoPush.h
-        UniformGridGPU_AA_MacroSetter.cpp UniformGridGPU_AA_MacroSetter.h
-        UniformGridGPU_AA_MacroGetter.cpp UniformGridGPU_AA_MacroGetter.h
-        UniformGridGPU_AA_Defines.h
-        )
-
-foreach(config srt trt mrt smagorinsky entropic )
-    waLBerla_add_executable ( NAME UniformGridBenchmarkGPU_AA_${config}
-            FILES UniformGridGPU_AA.cpp UniformGridGPU_AA.py
-            DEPENDS blockforest boundary core cuda domain_decomposition field geometry timeloop vtk gui
-            CODEGEN_CFG ${config})
-endforeach()
+
+foreach (config srt trt mrt smagorinsky entropic smagorinsky_noopt entropic_kbc_n4
+      entropic_kbc_n4_noopt mrt_noopt mrt_full mrt_full_noopt
+      cumulant cumulant_d3q27
+      srt_d3q27 mrt_d3q27 mrt_d3q27_noopt smagorinsky_d3q27 smagorinsky_d3q27_noopt mrt_full_d3q27 mrt_full_d3q27_noopt)
+
+    waLBerla_generate_target_from_python(NAME UniformGridGPUGenerated_${config}
+          FILE UniformGridGPU.py
+          CODEGEN_CFG ${config}
+          OUT_FILES UniformGridGPU_LatticeModel.cpp UniformGridGPU_LatticeModel.h
+          UniformGridGPU_NoSlip.cu UniformGridGPU_NoSlip.h
+          UniformGridGPU_UBB.cu UniformGridGPU_UBB.h
+          UniformGridGPU_PackInfo.cu UniformGridGPU_PackInfo.h
+          UniformGridGPU_MacroSetter.cpp UniformGridGPU_MacroSetter.h
+          UniformGridGPU_MacroGetter.cpp UniformGridGPU_MacroGetter.h
+          UniformGridGPU_Defines.h
+          )
+
+
+    waLBerla_add_executable(NAME UniformGridBenchmarkGPU_${config}
+          FILES UniformGridGPU.cpp
+          DEPENDS blockforest boundary core cuda domain_decomposition field geometry timeloop vtk gui UniformGridGPUGenerated_${config})
+endforeach ()
+
+
+foreach (config srt trt mrt smagorinsky entropic)
+
+    waLBerla_generate_target_from_python(NAME UniformGridGPUGenerated_AA_${config}
+          FILE UniformGridGPU_AA.py
+          CODEGEN_CFG ${config}
+          OUT_FILES UniformGridGPU_AA_PackInfoPull.cu UniformGridGPU_AA_PackInfoPull.h
+          UniformGridGPU_AA_PackInfoPush.cu UniformGridGPU_AA_PackInfoPush.h
+          UniformGridGPU_AA_MacroSetter.cpp UniformGridGPU_AA_MacroSetter.h
+          UniformGridGPU_AA_MacroGetter.cpp UniformGridGPU_AA_MacroGetter.h
+          UniformGridGPU_AA_Defines.h
+          )
+
+
+    waLBerla_add_executable(NAME UniformGridBenchmarkGPU_AA_${config}
+          FILES UniformGridGPU_AA.cpp
+          DEPENDS blockforest boundary core cuda domain_decomposition field geometry timeloop vtk gui UniformGridGPUGenerated_AA_${config})
+endforeach ()
diff --git a/apps/benchmarks/UniformGridGenerated/CMakeLists.txt b/apps/benchmarks/UniformGridGenerated/CMakeLists.txt
index 3204db7a4..018a12cab 100644
--- a/apps/benchmarks/UniformGridGenerated/CMakeLists.txt
+++ b/apps/benchmarks/UniformGridGenerated/CMakeLists.txt
@@ -2,17 +2,19 @@ waLBerla_link_files_to_builddir( "*.prm" )
 waLBerla_link_files_to_builddir( "*.py" )
 
 
-waLBerla_python_file_generates(UniformGridGenerated.py
-        GenMacroGetter.cpp GenMacroSetter.cpp
-        GenPackInfo.cpp GenPackInfoAAPush.cpp GenPackInfoAAPull.cpp
-        GenLbKernel.cpp GenLbKernelAAEven.cpp GenLbKernelAAOdd.cpp
-        GenMpiDtypeInfo.h GenMpiDtypeInfoAAPull.h GenMpiDtypeInfoAAPush.h
-        GenDefines.h)
-
-
 foreach(config trt smagorinsky mrt3 mrt entropic_kbc_n4 cumulant )
+    waLBerla_generate_target_from_python(NAME UniformGridGenerated_${config}
+          CODEGEN_CFG ${config}
+          FILE UniformGridGenerated.py
+          OUT_FILES GenMacroGetter.cpp GenMacroGetter.h GenMacroSetter.cpp GenMacroSetter.h
+          GenPackInfo.cpp GenPackInfo.h GenPackInfoAAPush.cpp GenPackInfoAAPush.h GenPackInfoAAPull.cpp GenPackInfoAAPull.h
+          GenLbKernel.cpp GenLbKernel.h GenLbKernelAAEven.cpp GenLbKernelAAEven.h GenLbKernelAAOdd.cpp GenLbKernelAAOdd.h
+          GenMpiDtypeInfo.h GenMpiDtypeInfoAAPull.h GenMpiDtypeInfoAAPush.h
+          GenDefines.h)
+
     waLBerla_add_executable ( NAME UniformGridBenchmarkGenerated_${config}
-            FILES UniformGridGenerated.cpp UniformGridGenerated.py
-            DEPENDS blockforest boundary core domain_decomposition field geometry timeloop vtk gui
-            CODEGEN_CFG ${config})
+          FILES UniformGridGenerated.cpp
+          DEPENDS blockforest boundary core domain_decomposition field geometry timeloop vtk gui
+          UniformGridGenerated_${config})
+
 endforeach()
diff --git a/cmake/waLBerlaFunctions.cmake b/cmake/waLBerlaFunctions.cmake
index 841051458..1d80e1af2 100644
--- a/cmake/waLBerlaFunctions.cmake
+++ b/cmake/waLBerlaFunctions.cmake
@@ -89,23 +89,13 @@ function ( waLBerla_add_module )
  	endforeach( )
 
     if ( hasSourceFiles )
-        set( generatedSourceFiles )
-        set( generatorSourceFiles )
-        handle_python_codegen(sourceFiles generatedSourceFiles generatorSourceFiles codeGenRequired "default_codegen" ${sourceFiles})
-        if( NOT WALBERLA_BUILD_WITH_CODEGEN AND codeGenRequired)
-            message(STATUS "Skipping ${ARG_NAME} since pystencils code generation is not enabled")
-            return()
-        endif()
-
         if ( CUDA_FOUND )
-            cuda_add_library( ${moduleLibraryName} STATIC ${sourceFiles} ${generatedSourceFiles} ${generatorSourceFiles} ${otherFiles} )
+            cuda_add_library( ${moduleLibraryName} STATIC ${sourceFiles} ${otherFiles} )
         else()
-            add_library( ${moduleLibraryName} STATIC ${sourceFiles} ${generatedSourceFiles} ${generatorSourceFiles} ${otherFiles} )
+            add_library( ${moduleLibraryName} STATIC ${sourceFiles} ${otherFiles} )
         endif( CUDA_FOUND )
-
-        set_source_files_properties( ${generatedSourceFiles} PROPERTIES GENERATED TRUE )
  	else( )
- 	   add_custom_target( ${moduleLibraryName} SOURCES ${sourceFiles} ${generatedSourceFiles} ${otherFiles} )  # dummy IDE target
+ 	   add_custom_target( ${moduleLibraryName} SOURCES ${sourceFiles} ${otherFiles} )  # dummy IDE target
  	endif( )
 
     waLBerla_register_dependency ( ${moduleName} ${ARG_DEPENDS} )
@@ -125,10 +115,6 @@ function ( waLBerla_add_module )
                                                ARCHIVE DESTINATION lib )
     endif( )
 
-    if( codeGenRequired)
-        target_include_directories(${ARG_NAME} PUBLIC "${CMAKE_CURRENT_BINARY_DIR}/default_codegen")
-    endif()
-
     # Install rule for header
     install ( DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/
               DESTINATION "walberla/${moduleName}"
@@ -169,7 +155,6 @@ endfunction ( waLBerla_add_module )
 #   FILES   [optional]    list of all source and header files belonging to that application
 #                         if this is not given, all source and header files in the directory are added.
 #                         Careful: when file was added or deleted, cmake has to be run again
-#   CODGEN_CFG [optional] string passed to code generation scripts
 #
 #  Example:
 #     waLBerla_compile_app ( NAME myApp DEPENDS core field lbm/boundary )
@@ -177,7 +162,7 @@ endfunction ( waLBerla_add_module )
 
 function ( waLBerla_add_executable )
     set( options )
-    set( oneValueArgs NAME GROUP CODEGEN_CFG)
+    set( oneValueArgs NAME GROUP)
     set( multiValueArgs FILES DEPENDS)
     cmake_parse_arguments( ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} )
 
@@ -185,10 +170,6 @@ function ( waLBerla_add_executable )
       message ( FATAL_ERROR "waLBerla_add_executable called without a NAME" )
     endif()
 
-    if( NOT ARG_CODEGEN_CFG)
-        set(ARG_CODEGEN_CFG "default_codegen")
-    endif()
-
     # Skip this app, if it depends on modules that have not been built ( because they for example depend on PE)
     foreach ( depMod ${ARG_DEPENDS} )
         get_module_library_name ( depModLibraryName ${depMod} )
@@ -220,26 +201,12 @@ function ( waLBerla_add_executable )
         endif ( )
     endif()
 
-    set( generatedSourceFiles )
-    set( generatorSourceFiles )
-    handle_python_codegen(sourceFiles generatedSourceFiles generatorSourceFiles
-                          codeGenRequired ${ARG_CODEGEN_CFG} ${sourceFiles} )
-
-    if( NOT WALBERLA_BUILD_WITH_CODEGEN AND codeGenRequired)
-        if( WALBERLA_LOG_SKIPPED )
-           message(STATUS "Skipping ${ARG_NAME} since pystencils code generation is not enabled")
-        endif()
-        return()
-    endif()
-
     if ( WALBERLA_BUILD_WITH_CUDA )
-        cuda_add_executable( ${ARG_NAME} ${sourceFiles} ${generatedSourceFiles} )
+        cuda_add_executable( ${ARG_NAME} ${sourceFiles} )
     else()
-        add_executable( ${ARG_NAME} ${sourceFiles} ${generatedSourceFiles}  )
+        add_executable( ${ARG_NAME} ${sourceFiles} )
     endif()
 
-    set_source_files_properties( ${generatedSourceFiles} PROPERTIES GENERATED TRUE )
-
     target_link_modules  ( ${ARG_NAME} ${ARG_DEPENDS}  )
     target_link_libraries( ${ARG_NAME} ${WALBERLA_LINK_LIBRARIES_KEYWORD} ${SERVICE_LIBS} )
     set_property( TARGET ${ARG_NAME} PROPERTY CXX_STANDARD 14 )
@@ -251,41 +218,10 @@ function ( waLBerla_add_executable )
         set_property( TARGET  ${ARG_NAME}  PROPERTY  FOLDER  ${ARG_GROUP} )
     endif()
 
-    if( codeGenRequired )
-        target_include_directories(${ARG_NAME} PUBLIC "${CMAKE_CURRENT_BINARY_DIR}/${ARG_CODEGEN_CFG}")
-    endif()
-
 endfunction ( waLBerla_add_executable )
 
 #######################################################################################################################
 
-
-
-
-#######################################################################################################################
-#
-# Function to tell CMake which C/C++/CUDA files are generated by a python file
-#
-# Example:
-#    waLBerla_python_file_generates(MyPythonCodeGenScript.py Sweep1.cpp Sweep2.cu)
-#
-#
-#######################################################################################################################
-function( waLBerla_python_file_generates pythonFile )
-    set(generatedFiles "")
-    foreach(element ${ARGN})
-        string(REGEX REPLACE "\\.[^.]*$" "" elementWithoutExtension ${element})
-        list(APPEND generatedFiles ${element})
-        list(APPEND generatedFiles "${elementWithoutExtension}.h")
-    endforeach()
-    list(REMOVE_DUPLICATES generatedFiles)
-    get_filename_component(pythonFileAbsolutePath ${pythonFile} ABSOLUTE)
-    set( "WALBERLA_CODEGEN_INFO_${pythonFileAbsolutePath}" ${generatedFiles}
-            CACHE INTERNAL "Files generated by python script ${pythonFile}" FORCE)
-endfunction(waLBerla_python_file_generates)
-
-
-
 #######################################################################################################################
 #
 # Adds a  waLBerla module test executable.
diff --git a/cmake/waLBerlaHelperFunctions.cmake b/cmake/waLBerlaHelperFunctions.cmake
index 21c9b01b5..74c6c9516 100644
--- a/cmake/waLBerlaHelperFunctions.cmake
+++ b/cmake/waLBerlaHelperFunctions.cmake
@@ -15,75 +15,77 @@ function ( add_flag  _VAR  _FLAG )
 endfunction ( add_flag )
 #######################################################################################################################
 
-
-
 #######################################################################################################################
 #
 # Function to handle python code generation files
 #
+# Example Usage:
+#     waLBerla_generate_target_from_python(NAME GeneratedLib
+#          FILE pythonScript.py
+#          CODEGEN_CFG condgen_dir
+#          OUT_FILES Kernel.h Kernel.cpp
+#          )
 # parameters:
-#  sourceFilesOut:  variable where source files without python files are written to
-#  generatedSourceFilesOut: variable where generated source files (with custom command) are written to
-#  generatorsOut: only the python files that have been passed
-#  codeGenRequired: true if at least one python file was part of the sources
+#  NAME: name of the target library that will be created
+#  FILE: the script which generates the C/C++ code
+#  OUT_FILES: list of all files that are generated by the script
+#  CODEGEN_CFG (optional) config string which is passed to the generator script
+#                         also specifies the directory where generated source files are written to. Default ist 'default_codegen'
 #
 # The list of generated files is determined via the pystencils_walberla package mechanism.
 # The python script, when called with -l, should return a semicolon-separated list of generated files
 # if this list changes, CMake has to be run manually again.
 #######################################################################################################################
-function( handle_python_codegen sourceFilesOut generatedSourceFilesOut generatorsOut codeGenRequiredOut codegenCfg)
-    set(result )
-    set(generatedResult )
-    set(generatorsResult )
-    set(codeGenRequired NO)
-    foreach( sourceFile ${ARGN} )
-        if( ${sourceFile} MATCHES ".*\\.py$" )
-            set(codeGenRequired YES)
-            if( WALBERLA_BUILD_WITH_CODEGEN)
-                get_filename_component(pythonFileAbsolutePath ${sourceFile} ABSOLUTE )
-                set( generatedSourceFiles ${WALBERLA_CODEGEN_INFO_${pythonFileAbsolutePath}} )
-
-                set( generatedWithAbsolutePath )
-                foreach( filename ${generatedSourceFiles} )
-                    list(APPEND generatedWithAbsolutePath ${CMAKE_CURRENT_BINARY_DIR}/${codegenCfg}/${filename})
-                endforeach()
-
-                list(APPEND generatedResult  ${generatedWithAbsolutePath} )
-                list(APPEND generatorsResult ${sourceFile} )
-
-                string (REPLACE ";" "\", \"" jsonFileList "${generatedWithAbsolutePath}" )
-                set(pythonParameters
-                        "\\\{\"EXPECTED_FILES\": [\"${jsonFileList}\"], \"CMAKE_VARS\" : \\\{  "
-                            "\"WALBERLA_OPTIMIZE_FOR_LOCALHOST\": \"${WALBERLA_OPTIMIZE_FOR_LOCALHOST}\","
-                            "\"WALBERLA_DOUBLE_ACCURACY\": \"${WALBERLA_DOUBLE_ACCURACY}\","
-                            "\"CODEGEN_CFG\": \"${codegenCfg}\","
-                            "\"WALBERLA_BUILD_WITH_MPI\": \"${WALBERLA_BUILD_WITH_MPI}\","
-                            "\"WALBERLA_BUILD_WITH_CUDA\": \"${WALBERLA_BUILD_WITH_CUDA}\","
-                            "\"WALBERLA_BUILD_WITH_OPENMP\": \"${WALBERLA_BUILD_WITH_OPENMP}\" \\\} \\\}"
-                        )
-                string(REPLACE "\"" "\\\"" pythonParameters ${pythonParameters})   # even one more quoting level required
-                string(REPLACE "\n" "" pythonParameters ${pythonParameters})  # remove newline characters
-
-                set( WALBERLA_PYTHON_DIR ${walberla_SOURCE_DIR}/python)
-                file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/${codegenCfg}")
-                add_custom_command(OUTPUT ${generatedWithAbsolutePath}
-                                   DEPENDS ${sourceFile}
-                                   COMMAND ${CMAKE_COMMAND} -E env PYTHONPATH=${WALBERLA_PYTHON_DIR} ${PYTHON_EXECUTABLE} ${sourceFile} ${pythonParameters}
-                                   WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/${codegenCfg}")
-            endif()
-        else()
-            list(APPEND result ${sourceFile})
+function( waLBerla_generate_target_from_python )
+    set( options )
+    set( oneValueArgs NAME FILE CODEGEN_CFG)
+    set( multiValueArgs OUT_FILES)
+    cmake_parse_arguments( PYGEN "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} )
+    if( NOT WALBERLA_BUILD_WITH_CODEGEN )
+        if( WALBERLA_LOG_SKIPPED )
+            message(STATUS "Skipping ${PYGEN_NAME} since pystencils code generation is not enabled")
         endif()
-    endforeach()
-    set( ${sourceFilesOut} ${result} PARENT_SCOPE )
-    set( ${generatedSourceFilesOut} ${generatedResult} PARENT_SCOPE )
-    set( ${generatorsOut} ${generatorsResult} PARENT_SCOPE )
-    set( ${codeGenRequiredOut} ${codeGenRequired} PARENT_SCOPE )
-endfunction ( handle_python_codegen )
-#######################################################################################################################
-
+        return()
+    endif()
 
+    #set(sourcefile ${PYGEN_FILE})
+    get_filename_component(sourceFile ${PYGEN_FILE} ABSOLUTE )
+    if (PYGEN_CODEGEN_CFG)
+        set( codegenCfg ${PYGEN_CODEGEN_CFG})
+    else()
+        set( codegenCfg "default_codegen")
+    endif()
+    set( generatedSourceFiles ${PYGEN_OUT_FILES} )
+    set( generatedWithAbsolutePath )
+    foreach( filename ${generatedSourceFiles} )
+        list(APPEND generatedWithAbsolutePath ${CMAKE_CURRENT_BINARY_DIR}/${codegenCfg}/${filename})
+    endforeach()
 
+    string (REPLACE ";" "\", \"" jsonFileList "${generatedWithAbsolutePath}" )
+    set(pythonParameters
+          "\\\{\"EXPECTED_FILES\": [\"${jsonFileList}\"], \"CMAKE_VARS\" : \\\{  "
+          "\"WALBERLA_OPTIMIZE_FOR_LOCALHOST\": \"${WALBERLA_OPTIMIZE_FOR_LOCALHOST}\","
+          "\"WALBERLA_DOUBLE_ACCURACY\": \"${WALBERLA_DOUBLE_ACCURACY}\","
+          "\"CODEGEN_CFG\": \"${codegenCfg}\","
+          "\"WALBERLA_BUILD_WITH_MPI\": \"${WALBERLA_BUILD_WITH_MPI}\","
+          "\"WALBERLA_BUILD_WITH_CUDA\": \"${WALBERLA_BUILD_WITH_CUDA}\","
+          "\"WALBERLA_BUILD_WITH_OPENMP\": \"${WALBERLA_BUILD_WITH_OPENMP}\" \\\} \\\}"
+          )
+    string(REPLACE "\"" "\\\"" pythonParameters ${pythonParameters})   # even one more quoting level required
+    string(REPLACE "\n" "" pythonParameters ${pythonParameters})  # remove newline characters
+
+    set( WALBERLA_PYTHON_DIR ${walberla_SOURCE_DIR}/python)
+    file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/${codegenCfg}")
+
+    add_custom_command(OUTPUT ${generatedWithAbsolutePath}
+          DEPENDS ${sourceFile}
+          COMMAND ${CMAKE_COMMAND} -E env PYTHONPATH=${WALBERLA_PYTHON_DIR} ${PYTHON_EXECUTABLE} ${sourceFile} ${pythonParameters}
+          WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/${codegenCfg}")
+
+    add_library(${PYGEN_NAME} ${generatedWithAbsolutePath})
+    target_include_directories(${PYGEN_NAME} PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/${codegenCfg})
+endfunction()
+#######################################################################################################################
 
 #######################################################################################################################
 #
diff --git a/python/pystencils_walberla/cmake_integration.py b/python/pystencils_walberla/cmake_integration.py
index 4bc5586af..6b0ce87e3 100644
--- a/python/pystencils_walberla/cmake_integration.py
+++ b/python/pystencils_walberla/cmake_integration.py
@@ -32,7 +32,7 @@ class CodeGeneration:
             written = set(os.path.realpath(f) for f in self.context.files_written)
             only_in_cmake = expected - written
             only_generated = written - expected
-            error_message = "Generated files specified not correctly in cmake with 'waLBerla_python_file_generates'\n"
+            error_message = "Generated files (OUT_FILES) specified not correctly in cmake with 'waLBerla_generate_target_from_python'\n"
             if only_in_cmake:
                 error_message += "Files only specified in CMake {}\n".format(
                     [os.path.basename(p) for p in only_in_cmake])
diff --git a/tests/cuda/CMakeLists.txt b/tests/cuda/CMakeLists.txt
index 6fdeb8352..8f2a4a3e9 100644
--- a/tests/cuda/CMakeLists.txt
+++ b/tests/cuda/CMakeLists.txt
@@ -4,6 +4,8 @@
 #
 ###################################################################################################
 
+if( WALBERLA_BUILD_WITH_CUDA )
+
 waLBerla_compile_test( FILES communication/GPUPackInfoTest.cpp DEPENDS blockforest )
 waLBerla_execute_test( NAME  GPUPackInfoTest )
 
@@ -20,12 +22,11 @@ waLBerla_compile_test( FILES FieldIndexing3DTest.cpp FieldIndexing3DTest.cu )
 waLBerla_execute_test( NAME  FieldIndexing3DTest )
 
 
-waLBerla_python_file_generates(codegen/CudaJacobiKernel.py
-        CudaJacobiKernel2D.cu CudaJacobiKernel2D.h
-        CudaJacobiKernel3D.cu CudaJacobiKernel3D.h)
+waLBerla_generate_target_from_python(NAME CodegenJacobiGPUGeneratedCudaJacobiKernel FILE codegen/CudaJacobiKernel.py
+      OUT_FILES CudaJacobiKernel2D.cu CudaJacobiKernel2D.h
+      CudaJacobiKernel3D.cu CudaJacobiKernel3D.h)
 waLBerla_compile_test( FILES codegen/CodegenJacobiGPU.cpp
-                             codegen/CudaJacobiKernel.py
-                       DEPENDS blockforest timeloop gui )
+                       DEPENDS blockforest timeloop gui CodegenJacobiGPUGeneratedCudaJacobiKernel )
 waLBerla_execute_test( NAME CodegenJacobiGPU )
 
 
@@ -38,5 +39,8 @@ waLBerla_compile_test( FILES CudaMPI DEPENDS blockforest timeloop gui )
 
 waLBerla_compile_test( FILES AlignmentTest.cpp DEPENDS blockforest timeloop )
 
-waLBerla_python_file_generates(codegen/MicroBenchmarkGpuLbm.py MicroBenchmarkStreamKernel.cu MicroBenchmarkCopyKernel.cu)
-waLBerla_compile_test( FILES codegen/MicroBenchmarkGpuLbm.cpp codegen/MicroBenchmarkGpuLbm.py)
+waLBerla_generate_target_from_python(NAME MicroBenchmarkGpuLbmGenerated FILE codegen/MicroBenchmarkGpuLbm.py
+      OUT_FILES MicroBenchmarkStreamKernel.cu MicroBenchmarkCopyKernel.cu)
+waLBerla_compile_test( FILES codegen/MicroBenchmarkGpuLbm.cpp DEPENDS MicroBenchmarkGpuLbmGenerated)
+
+endif()
\ No newline at end of file
diff --git a/tests/field/CMakeLists.txt b/tests/field/CMakeLists.txt
index 6a6b8546d..bcb806a0d 100644
--- a/tests/field/CMakeLists.txt
+++ b/tests/field/CMakeLists.txt
@@ -63,8 +63,9 @@ endif( WALBERLA_BUILD_WITH_MPI )
 
 # CodeGen Tests
 
-waLBerla_python_file_generates(codegen/JacobiKernel.py JacobiKernel2D.cpp JacobiKernel3D.cpp )
-waLBerla_compile_test( FILES codegen/CodegenJacobiCPU.cpp codegen/JacobiKernel.py DEPENDS gui timeloop )
+waLBerla_generate_target_from_python(NAME CodegenJacobiCPUGeneratedJacobiKernel FILE codegen/JacobiKernel.py
+      OUT_FILES JacobiKernel2D.cpp JacobiKernel2D.h JacobiKernel3D.cpp JacobiKernel3D.h )
+waLBerla_compile_test( FILES codegen/CodegenJacobiCPU.cpp DEPENDS gui timeloop CodegenJacobiCPUGeneratedJacobiKernel)
 waLBerla_execute_test( NAME CodegenJacobiCPU )
 
 
diff --git a/tests/lbm/CMakeLists.txt b/tests/lbm/CMakeLists.txt
index 7ffa9167b..c75abc941 100644
--- a/tests/lbm/CMakeLists.txt
+++ b/tests/lbm/CMakeLists.txt
@@ -71,13 +71,12 @@ waLBerla_compile_test( FILES SuViscoelasticityTest.cpp DEPENDS field blockforest
 waLBerla_execute_test( NAME  SuViscoelasticityTest COMMAND $<TARGET_FILE:SuViscoelasticityTest> ${CMAKE_CURRENT_SOURCE_DIR}/Su.prm )
 
 # Code Generation
-waLBerla_python_file_generates(codegen/LbCodeGenerationExample.py
-                              LbCodeGenerationExample_LatticeModel.cpp
-                              LbCodeGenerationExample_NoSlip.cpp
-                              LbCodeGenerationExample_UBB.cpp )
-waLBerla_compile_test( FILES codegen/LbCodeGenerationExample.py
-                             codegen/LbCodeGenerationExample.cpp)
-waLBerla_python_file_generates(codegen/FluctuatingMRT.py
-                              FluctuatingMRT_LatticeModel.cpp )
-waLBerla_compile_test( FILES codegen/FluctuatingMRT.py
-                             codegen/FluctuatingMRT.cpp)
\ No newline at end of file
+waLBerla_generate_target_from_python(NAME LbCodeGenerationExampleGenerated
+      FILE codegen/LbCodeGenerationExample.py
+      OUT_FILES LbCodeGenerationExample_LatticeModel.cpp LbCodeGenerationExample_LatticeModel.h
+      LbCodeGenerationExample_NoSlip.cpp LbCodeGenerationExample_NoSlip.h
+      LbCodeGenerationExample_UBB.cpp LbCodeGenerationExample_UBB.h )
+waLBerla_compile_test( FILES codegen/LbCodeGenerationExample.cpp DEPENDS LbCodeGenerationExampleGenerated)
+waLBerla_generate_target_from_python(NAME FluctuatingMRTGenerated FILE codegen/FluctuatingMRT.py
+                              OUT_FILES FluctuatingMRT_LatticeModel.cpp FluctuatingMRT_LatticeModel.h )
+waLBerla_compile_test( FILES codegen/FluctuatingMRT.cpp DEPENDS FluctuatingMRTGenerated)
\ No newline at end of file
-- 
GitLab