diff --git a/CMakeLists.txt b/CMakeLists.txt index 4c5eb03460d176c992c410bfa1e7f4be01231d3f..72788381e46622204d379f5e53ff3e54414b0fe2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,6 +8,6 @@ set(CMAKE_CXX_STANDARD_REQUIRED) list (APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake ) -include( PrepareSFG ) +include( WalberlaCodegen ) add_subdirectory( lib ) diff --git a/cmake/ManageCodegenVenv.py b/cmake/ManageCodegenVenv.py new file mode 100644 index 0000000000000000000000000000000000000000..77f515dbbc28672fc8fa4f3687f753fb04cdf961 --- /dev/null +++ b/cmake/ManageCodegenVenv.py @@ -0,0 +1,101 @@ +from __future__ import annotations + +from typing import Generator +import sys +import subprocess +import json +import shutil +from contextlib import contextmanager +from dataclasses import dataclass, asdict +from argparse import ArgumentParser +from pathlib import Path + + +@dataclass +class VenvState: + venv_dir: str | None = None + main_requirements_file: str | None = None + initialized: bool = False + + @staticmethod + @contextmanager + def lock(statefile: Path) -> Generator[VenvState, None, None]: + statefile_bak = statefile.with_suffix(".json.bak") + if statefile.exists(): + statefile.replace(statefile_bak) + + with statefile_bak.open("r") as f: + state_dict = json.load(f) + + state = VenvState(**state_dict) + else: + state = VenvState() + + yield state + + # If the consumer raises an error, execution terminates here + + state_dict = asdict(state) + with statefile_bak.open("w") as f: + json.dump(state_dict, f) + statefile_bak.replace(statefile) + + +def action_initialize(args): + statefile = Path(args.statefile) + + with VenvState.lock(statefile) as state: + if not state.initialized: + p_venv_dir = Path(args.venv_dir).resolve() + if p_venv_dir.exists(): + shutil.rmtree(p_venv_dir) + reqs_file = Path(args.requirements_file).resolve() + + state.venv_dir = str(p_venv_dir) + state.main_requirements_file = str(reqs_file) + + base_py = Path(sys.executable) + + # Create the virtual environment + venv_args = [base_py, "-m", "venv", state.venv_dir] + subprocess.run(venv_args).check_returncode() + + # Install base requirements + venv_py = Path(state.venv_dir).absolute() / "bin" / "python" + install_args = [venv_py, "-m", "pip", "install", "-r", state.main_requirements_file] + subprocess.run(install_args).check_returncode() + + state.initialized = True + + +def main(): + parser = ArgumentParser("ManageCodegenVenv") + parser.add_argument( + "-s", + "--statefile", + required=True, + dest="statefile", + help="Path to the environment statefile", + ) + + subparsers = parser.add_subparsers(required=True) + + parser_initialize = subparsers.add_parser("init") + parser_initialize.add_argument( + "venv_dir", + type=str, + help="Location of the virtual environment in the filesystem", + ) + parser_initialize.add_argument( + "requirements_file", + type=str, + help="Location of the virtual environment in the filesystem", + ) + parser_initialize.set_defaults(func=action_initialize) + + args = parser.parse_args() + args.func(args) + + +if __name__ == "__main__": + main() diff --git a/cmake/PrepareSFG.cmake b/cmake/WalberlaCodegen.cmake similarity index 75% rename from cmake/PrepareSFG.cmake rename to cmake/WalberlaCodegen.cmake index d1c21ac62527d08de3677e59f04af398ccca6e2c..435af24f9b9f88f3e81b99261b17ef4cac95f4ec 100644 --- a/cmake/PrepareSFG.cmake +++ b/cmake/WalberlaCodegen.cmake @@ -9,10 +9,22 @@ set( if( WALBERLA_CODEGEN_PRIVATE_VENV ) set(WALBERLA_CODEGEN_VENV_PATH ${CMAKE_CURRENT_BINARY_DIR}/codegen-venv CACHE PATH "Location of the virtual environment used for code generation") set(_venv_python_exe ${WALBERLA_CODEGEN_VENV_PATH}/bin/python) + set( + _WALBERLA_CODEGEN_VENV_MANAGER + ${sfg_walberla_SOURCE_DIR}/cmake/ManageCodegenVenv.py + CACHE INTERNAL + "venv manager filepath - for internal use only" + ) + set( + _WALBERLA_CODEGEN_VENV_STATEFILE + ${CMAKE_CURRENT_BINARY_DIR}/walberla-venv-state.json + CACHE INTERNAL + "venv statefile - for internal use only" + ) set( WALBERLA_CODEGEN_VENV_REQUIREMENTS - ${sfg_walberla_SOURCE_DIR}/codegen-requirements.txt + ${sfg_walberla_SOURCE_DIR}/cmake/codegen-requirements.txt.in CACHE PATH "Location of the primary requirements file for the codegen virtual environment" ) @@ -20,32 +32,32 @@ if( WALBERLA_CODEGEN_PRIVATE_VENV ) find_package( Python COMPONENTS Interpreter REQUIRED ) - if(NOT _sfg_private_venv_done) - message( STATUS "Setting up Python virtual environment at ${WALBERLA_CODEGEN_VENV_PATH}" ) - - # Create the venv and register its interpreter with pystencils-sfg - if(NOT EXISTS ${WALBERLA_CODEGEN_VENV_PATH}) - execute_process( - COMMAND ${Python_EXECUTABLE} -m venv ${WALBERLA_CODEGEN_VENV_PATH} - ) - endif() - - message( STATUS "Installing required Python packages..." ) - - execute_process( - COMMAND ${_venv_python_exe} -m pip install -r $CACHE{WALBERLA_CODEGEN_VENV_REQUIREMENTS} - OUTPUT_QUIET - ) - - execute_process( - COMMAND ${_venv_python_exe} -m pip install -e ${sfg_walberla_SOURCE_DIR} - OUTPUT_QUIET - ) - - set( _sfg_private_venv_done TRUE CACHE BOOL "" ) - set( _wlb_codegen_python_init ${_venv_python_exe} ) - mark_as_advanced(_sfg_private_venv_done) + set( + _requirements_file + ${CMAKE_CURRENT_BINARY_DIR}/codegen-requirements.txt + ) + + configure_file( + ${WALBERLA_CODEGEN_VENV_REQUIREMENTS} + ${_requirements_file} + ) + + execute_process( + COMMAND + ${Python_EXECUTABLE} + ${_WALBERLA_CODEGEN_VENV_MANAGER} + -s ${_WALBERLA_CODEGEN_VENV_STATEFILE} + init + ${WALBERLA_CODEGEN_VENV_PATH} + ${_requirements_file} + RESULT_VARIABLE _lastResult + ) + + if( ${_lastResult} ) + message( FATAL_ERROR "Codegen virtual environment setup failed" ) endif() + + set( _wlb_codegen_python_init ${_venv_python_exe} ) else() # Use the external Python environment, but check if all packages are installed find_package( Python COMPONENTS Interpreter REQUIRED ) diff --git a/codegen-requirements.txt b/cmake/codegen-requirements.txt.in similarity index 63% rename from codegen-requirements.txt rename to cmake/codegen-requirements.txt.in index c7448f6d81ca0108bd13aca4d5762283dfd23124..07abf2e737e6479890fc92e3898266c65b3246cf 100644 --- a/codegen-requirements.txt +++ b/cmake/codegen-requirements.txt.in @@ -1,8 +1,8 @@ -# pystencils 2.0 Development Branch + git+https://i10git.cs.fau.de/pycodegen/pystencils.git@v2.0-dev -# lbmpy: feature branch for pystencils-2.0 compatibility git+https://i10git.cs.fau.de/pycodegen/lbmpy.git@fhennig/pystencils2.0-compat -# pystencils-sfg: master git+https://i10git.cs.fau.de/pycodegen/pystencils-sfg.git + +-e ${sfg_walberla_SOURCE_DIR}