(guide_project_integration)=
# Project and Build System Integration

(compile_generated_files)=
## Compiling Generated Files

When compiling generated files that include pystencils kernels,
the pystencils runtime headers must be added to the compiler's include search path.
To get the right path, use the `pystencils.include` utility, e.g.:

```{code-block} bash
g++ -I $(python -m pystencils.include -s) [args...] [input-files...]
```

(config_module)=
## Project-Wide Settings using Configuration Modules

When embedding *pystencils-sfg* into a C++ project or build system,
you might want to set a project-wide base configuration for all generator scripts.
In addition, it might be necessary to pass various details about the project
and build setup to the generator scripts.
Both can be achieved by the use of a *configuration module*.

A configuration module is a Python file that defines up to two functions:
- `def configure_sfg(cfg: SfgConfig)` is called to set up the project-wide base configuration.
   It takes an {any}`SfgConfig` object which it may modify to establish the project-wide option set.
- `def project_info() -> Any` is called by *pystencils-sfg* to retrieve an object that encapsulates
  any custom project-specific information.
  This information is passed on to the generator scripts through
  the {any}`sfg.context.project_info <SfgContext.project_info>` attribute.

An example configuration module might look like this:

```Python
from pystencilssfg import SfgConfig

def configure_sfg(cfg: SfgConfig):
    cfg.extensions.header = "h++"
    cfg.extensions.impl = "c++"
    cfg.clang_format.code_style = "llvm"
    ...

def project_info():
    return {
        "project_name": "my-project",
        "float_precision": "float32",
        "use_cuda": False,
        ...
    }
```

Here, `project_info` returns a dictionary, but this is just for illustration;
the function may return any type of arbitrarily complex objects.
For improved API safety, {any}`dataclasses` might be a good tool for setting up
project info objects.

When invoking a generator script, the path to the current configuration module must be passed to it
using the `--sfg-config-module` command-line parameter.
This can be automated by an adequately set up build system, such as GNU Make or CMake.

If you are using pystencils-sfg with CMake through the provided CMake module,
[see below](#cmake_set_config_module) on how to specify a configuration module for your project.

(cmake_integration)=
## CMake Integration

*pystencils-sfg* is shipped with a CMake module for on-the-fly code generation during the CMake build process.

### Add the module

To include the module in your CMake source tree, you must first add the pystencils-sfg *Find-module*
to your CMake module path.
To create the Find-module, navigate to the directory it should be placed in and run the following command:

```shell
sfg-cli cmake make-find-module
```

This will create the `FindPystencilsSfg.cmake` file.
Make sure that its containing directory is added to the CMake module path.

To load pystencils-sfg into CMake, we first need to set the Python interpreter
of the environment SFG is installed in.
There are several ways of doing this:

#### Set Python via a Find-Module Hint

Set the `PystencilsSfg_PYTHON_PATH` hint variable inside your `CMakeLists.txt` to point at the
Python executable which should be used to invoke pystencils-sfg, e.g.:

```CMake
set(PystencilsSfg_PYTHON_PATH ${CMAKE_SOURCE_DIR}/.venv/bin/python)
```

This is the recommended way, especially when other parts of your project also use Python.

#### Set Python via a Cache Variable

On the command line or in a [CMake configure preset](https://cmake.org/cmake/help/latest/manual/cmake-presets.7.html),
set the `PystencilsSfg_PYTHON_INTERPRETER` cache variable to point at the Python executable to be used to invoke pystencils-sfg;
e.g.:

```bash
cmake -S . -B build -DPystencilsSfg_PYTHON_INTERPRETER=`pwd`/.venv/bin/python
```

If both the cache variable and the `PystencilsSfg_PYTHON_PATH` hint are set, the cache variable takes precedence,
so you can use the cache variable to override the hint.

#### Automatically Find a Python Installation

If none of the above is provided, pystencils-sfg will invoke [FindPython](https://cmake.org/cmake/help/latest/module/FindPython.html)
to determine the Python interpreter it should use.
You can affect this process through any of the hints listed in the `FindPython` documentation.

#### Find pystencils-sfg

Finally, call `find_package( PystencilsSfg )` from your `CMakeLists.txt` to load the SFG module.
If SFG as a dependency is not optional, add the `REQUIRED` flag such that the call will fail if
the package cannot be found.

(cmake_add_generator_scripts)=
### Adding Generator Scripts

The primary interaction point in CMake is the function `pystencilssfg_generate_target_sources`,
with the following signature:

```CMake
pystencilssfg_generate_target_sources( <target> 
    SCRIPTS script1.py [script2.py ...]
    [SCRIPT_ARGS arg1 [arg2 ...]]
    [DEPENDS dependency1.py [dependency2.py...]]
    [FILE_EXTENSIONS <header-extension> <impl-extension>]
    [CONFIG_MODULE <path-to-config-module.py>]
    [OUTPUT_DIRECTORY <output-directory>]
    [HEADER_ONLY]
)
```

It registers the generator scripts `script1.py [script2.py ...]` to be executed at compile time using `add_custom_command`
and adds their output files to the specified `<target>`.
Any changes in the generator scripts, or any listed dependency, will trigger regeneration.
The function takes the following options:

 - `SCRIPTS`: A list of generator scripts
 - `SCRIPT_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
 - `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))
 - `OUTPUT_DIRECTORY`: Custom output directory for generated files. If `OUTPUT_DIRECTORY` is a relative path,
   it will be interpreted relative to the current build directory.
 - `HEADER_ONLY`: If this option is set, instruct the generator scripts to only generate header files
   (see {any}`SfgConfig.header_only`).

If `OUTPUT_DIRECTORY` is *not* specified, any C++ header files generated by the above call
can be included in any files belonging to `target` via:

```C++
#include "gen/<file1.hpp>"
#include "gen/<file2.hpp>"
/* ... */
```

:::{attention}
If you change the code generator output directory using the `OUTPUT_DIRECTORY` argument,
you are yourself responsible for placing that directory--or any of its parents--on the
include path of your target.
:::

:::{admonition} Use with CMake 3.31 `codegen` target
:class: note

CMake 3.31 introduced the global `codegen` target, which allows invoking all code generation systems
working through [`add_custom_command`](https://cmake.org/cmake/help/latest/command/add_custom_command.html)
while bypassing the rest of the build tree.

When [policy CMP0171](https://cmake.org/cmake/help/latest/policy/CMP0171.html) is set to `NEW`, pystencils-sfg
will make use of this and add all generated sources to the global `codegen` target.
They can then be generated all-at-once by building `codegen`.

:::


(cmake_set_config_module)=
### Set a Configuration Module

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.
For this purpose, take a look at the
[configure_file](https://cmake.org/cmake/help/latest/command/configure_file.html) CMake function.
