diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8e1d8304deb33f586e6000cb4451a59ca00e5390..965549e79f89e50a1ee4303a7d2b1fd071f5b84a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -30,5 +30,7 @@ execute_process(COMMAND git submodule update --init --recursive
 # the python dependencies there.
 add_subdirectory(python)
 
+add_subdirectory(test)
+
 # finalize the dune project, e.g. generating config.h etc.
 finalize_dune_project(GENERATE_CONFIG_H_CMAKE)
diff --git a/cmake/modules/DunePerftoolMacros.cmake b/cmake/modules/DunePerftoolMacros.cmake
index 613dfb664b75999b008f0003a4d7c409cbf409ad..dc5b66fce6cd26402597f5bd7dbd83a16d54c1ba 100644
--- a/cmake/modules/DunePerftoolMacros.cmake
+++ b/cmake/modules/DunePerftoolMacros.cmake
@@ -1 +1,99 @@
 # File for module specific CMake tests.
+#
+# .. cmake_function:: add_generated_executable
+#
+#    .. cmake_param:: UFLFILE
+#       :single:
+#       :required:
+#
+#       The UFL file to create the executable from.
+#
+#    .. cmake_param:: TARGET
+#       :single:
+#       :required:
+#
+#       The name given to the added executable target.
+#
+#    .. cmake_param:: OPERATOR
+#       :single:
+#
+#       The local operator file name to generate. Defaults
+#       to a suitably mangled, but not easily readable name.
+#
+#    .. cmake_param:: DRIVER
+#       :single:
+#
+#       The driver file name to generate. Defaults
+#       to a suitably mangled, but not easily readable name.
+#
+#    .. cmake_param:: MAIN
+#
+#       The main source file to generate. Defaults
+#       to a suitably mangled, but not easily readable name.
+#
+#    .. cmake_param:: FORM_COMPILER_ARGS
+#       :multi:
+#       :argname arg:
+#
+#       Additional arguments as recognized by the form compiler.
+#
+#    Add an executable to the project that gets automatically
+#    generated at configure time with the form compiler uf2pdelab.
+#    Regeneration is triggered correctly if the UFL file or the
+#    form compiler changed.
+#
+
+function(add_generated_executable)
+  set(OPTIONS)
+  set(SINGLE TARGET OPERATOR DRIVER UFLFILE)
+  set(MULTI FORM_COMPILER_FLAGS)
+  include(CMakeParseArguments)
+  cmake_parse_arguments(GEN "${OPTIONS}" "${SINGLE}" "${MULTI}" ${ARGN})
+
+  if(GEN_UNPARSED_ARGUMENTS)
+    message("Unrecognized arguments in add_generated_executable. This usually indicates a typo.")
+  endif()
+
+  # Apply defaults and enforce requirements
+  if(NOT GEN_TARGET)
+    message(FATAL_ERROR "Need to specify the TARGET parameter for add_generated_executable")
+  endif()
+  if(NOT GEN_UFLFILE)
+    message(FATAL_ERROR "Need to specify the UFLFILE parameter for add_generated_executable")
+  endif()
+  if(NOT IS_ABSOLUTE GEN_UFLFILE)
+    set(GEN_UFLFILE ${CMAKE_CURRENT_SOURCE_DIR}/${GEN_UFLFILE})
+  endif()
+  if(NOT GEN_OPERATOR)
+    set(GEN_OPERATOR ${GEN_UFLFILE}_operator.hh)
+    string(REPLACE "${CMAKE_SOURCE_DIR}" "" GEN_OPERATOR ${GEN_OPERATOR})
+    string(REPLACE "/" "_" GEN_OPERATOR ${GEN_OPERATOR})
+    set(GEN_OPERATOR ${CMAKE_CURRENT_BINARY_DIR}/${GEN_OPERATOR})
+  endif()
+  if(NOT GEN_DRIVER)
+    set(GEN_DRIVER ${GEN_UFLFILE}_driver.hh)
+    string(REPLACE "${CMAKE_SOURCE_DIR}" "" GEN_DRIVER ${GEN_DRIVER})
+    string(REPLACE "/" "_" GEN_DRIVER ${GEN_DRIVER})
+    set(GEN_DRIVER ${CMAKE_CURRENT_BINARY_DIR}/${GEN_DRIVER})
+  endif()
+  if(NOT GEN_MAIN)
+    set(GEN_MAIN ${GEN_UFLFILE}_main.cc)
+    string(REPLACE "${CMAKE_SOURCE_DIR}" "" GEN_MAIN ${GEN_MAIN})
+    string(REPLACE "/" "_" GEN_MAIN ${GEN_MAIN})
+    set(GEN_MAIN ${CMAKE_CURRENT_BINARY_DIR}/${GEN_MAIN})
+  endif()
+
+  # Write a standard main function
+  configure_file(${CMAKE_SOURCE_DIR}/cmake/modules/StandardMain.cmake ${GEN_MAIN})
+
+  add_custom_command(OUTPUT ${GEN_OPERATOR} ${GEN_DRIVER}
+                     COMMAND ${CMAKE_BINARY_DIR}/dune-env-2 ufl2pdelab
+                             --operator-file ${GEN_OPERATOR}
+                             --driver-file ${GEN_DRIVER}
+                             ${GEN_FORM_COMPILER_ARGS}
+                             ${GEN_UFLFILE}
+                     COMMENT "Running ufl2pdelab for the target ${GEN_TARGET}"
+                    )
+
+  add_executable(${GEN_TARGET} ${GEN_MAIN} ${GEN_OPERATOR} ${GEN_DRIVER})
+endfunction()
diff --git a/cmake/modules/StandardMain.cmake b/cmake/modules/StandardMain.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..892fc4a47a78a6c09c7f4315c5a35454760d71fd
--- /dev/null
+++ b/cmake/modules/StandardMain.cmake
@@ -0,0 +1,31 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <dune/common/parallel/mpihelper.hh>
+#include <dune/common/exceptions.hh>
+
+#include"@GEN_DRIVER@"
+
+int main(int argc, char** argv)
+{
+  try{
+    //Maybe initialize Mpi
+    Dune::MPIHelper& helper = Dune::MPIHelper::instance(argc, argv);
+    if(Dune::MPIHelper::isFake)
+      std::cout<< "This is a sequential program." << std::endl;
+    else
+      std::cout<<"I am rank "<<helper.rank()<<" of "<<helper.size()
+        <<" processes!"<<std::endl;
+
+    driver(argc, argv);
+
+    return 0;
+  }
+  catch (Dune::Exception &e){
+    std::cerr << "Dune reported error: " << e << std::endl;
+  }
+  catch (...){
+    std::cerr << "Unknown exception thrown!" << std::endl;
+  }
+}
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..48c409ab4f1465cd40737ef03cb070b9e55a2f52
--- /dev/null
+++ b/test/CMakeLists.txt
@@ -0,0 +1,2 @@
+add_generated_executable(UFLFILE ../examples/laplace.ufl
+                         TARGET laplace)
\ No newline at end of file