diff --git a/CMakeLists.txt b/CMakeLists.txt index f7ff0378..ca4415be 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,10 @@ # CMake build configuration for Access Layer core cmake_minimum_required(VERSION 3.21) +if(POLICY CMP0144) + cmake_policy(SET CMP0144 NEW) +endif() + if(${CMAKE_VERSION} VERSION_GREATER_EQUAL 3.30) cmake_policy(SET CMP0167 NEW) # Use system BoostConfig instead of cmake FindBoost endif() @@ -81,7 +85,42 @@ endif() # ############################################################################## if(WIN32) - find_package(PThreads4W CONFIG REQUIRED) + # Ensure vcpkg paths are in CMAKE_PREFIX_PATH for finding packages + if(DEFINED VCPKG_INSTALLED_DIR AND DEFINED VCPKG_TARGET_TRIPLET) + list(APPEND CMAKE_PREFIX_PATH "${VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}") + message(STATUS "al-core: Added CMAKE_PREFIX_PATH: ${VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}") + else() + # Try to auto-detect vcpkg installed directory from build path + if(CMAKE_CURRENT_BINARY_DIR MATCHES "(.*/build)/") + set(_BUILD_DIR "${CMAKE_MATCH_1}") + set(_VCPKG_PATH "${_BUILD_DIR}/vcpkg_installed/x64-windows") + if(EXISTS "${_VCPKG_PATH}") + list(APPEND CMAKE_PREFIX_PATH "${_VCPKG_PATH}") + message(STATUS "al-core: Auto-detected vcpkg path: ${_VCPKG_PATH}") + + # Set PKG_CONFIG_EXECUTABLE for vcpkg's pkgconf + if(EXISTS "${_VCPKG_PATH}/tools/pkgconf/pkgconf.exe") + set(PKG_CONFIG_EXECUTABLE "${_VCPKG_PATH}/tools/pkgconf/pkgconf.exe" CACHE FILEPATH "pkg-config executable") + message(STATUS "al-core: Set PKG_CONFIG_EXECUTABLE to ${PKG_CONFIG_EXECUTABLE}") + endif() + endif() + endif() + endif() + + message(STATUS "al-core: CMAKE_PREFIX_PATH = ${CMAKE_PREFIX_PATH}") + + # Add cmake module path for FindPThreads4W.cmake fallback + list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/common/cmake") + + # Try CONFIG mode first, fall back to MODULE mode + find_package(PThreads4W CONFIG QUIET) + if(NOT PThreads4W_FOUND) + message(STATUS "PThreads4W CONFIG not found, trying MODULE mode") + find_package(PThreads4W MODULE REQUIRED) + else() + message(STATUS "Found PThreads4W via CONFIG mode") + endif() + find_package(dlfcn-win32 CONFIG REQUIRED) endif() @@ -115,6 +154,7 @@ set(PUBLIC_HEADER_FILES include/readback_plugin_feature.h include/uri_parser.h include/data_interpolation.h + include/fix_include_windows.h # al_defs.h is generated in the binary folder with configure_file: ${CMAKE_CURRENT_BINARY_DIR}/include/al_defs.h) diff --git a/common/cmake/ALBuildDataDictionary.cmake b/common/cmake/ALBuildDataDictionary.cmake index 6aff7f7a..7961b623 100644 --- a/common/cmake/ALBuildDataDictionary.cmake +++ b/common/cmake/ALBuildDataDictionary.cmake @@ -11,18 +11,95 @@ if( AL_DOCS_ONLY ) endif() # Find Python for the xsltproc.py program -find_package(Python REQUIRED COMPONENTS Interpreter Development.Module) -# Find LibXslt for the xsltproc program -find_package( LibXslt QUIET ) -if( NOT LIBXSLT_XSLTPROC_EXECUTABLE ) - message( FATAL_ERROR "Could not find xsltproc" ) +if(WIN32) + if(NOT Python3_FOUND AND NOT PYTHON_EXECUTABLE) + # Check if Python is in PATH + find_program(PYTHON_EXECUTABLE NAMES python3.exe python.exe python3 python DOC "Python interpreter") + if(NOT PYTHON_EXECUTABLE) + message(FATAL_ERROR "Could not find Python. Please ensure Python is installed and in PATH.") + endif() + else() + set(PYTHON_EXECUTABLE ${Python3_EXECUTABLE}) + endif() +else() + find_package(Python REQUIRED COMPONENTS Interpreter Development.Module) + set(PYTHON_EXECUTABLE ${Python_EXECUTABLE}) +endif() + +message(STATUS "Found Python: ${PYTHON_EXECUTABLE}") + +# Set up Python venv paths for saxonche (used for all XSLT transformations) +if(WIN32) + set(_VENV_PYTHON "${CMAKE_CURRENT_BINARY_DIR}/dd_build_env/Scripts/python.exe") + set(_VENV_PIP "${CMAKE_CURRENT_BINARY_DIR}/dd_build_env/Scripts/pip.exe") +else() + set(_VENV_PYTHON "${CMAKE_CURRENT_BINARY_DIR}/dd_build_env/bin/python") + set(_VENV_PIP "${CMAKE_CURRENT_BINARY_DIR}/dd_build_env/bin/pip") endif() if( NOT AL_DOWNLOAD_DEPENDENCIES AND NOT AL_DEVELOPMENT_LAYOUT ) # The DD easybuild module should be loaded, use that module: - # Use idsinfo idspath command to get the path to IDSDef.xml or data_dictionary.xml + # Create Python venv first and install imas_data_dictionary + if(NOT EXISTS "${_VENV_PYTHON}") + execute_process( + COMMAND ${PYTHON_EXECUTABLE} -m venv dd_build_env + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + RESULT_VARIABLE _VENV_EXITCODE + OUTPUT_VARIABLE _VENV_OUTPUT + ERROR_VARIABLE _VENV_ERROR + ) + + if(_VENV_EXITCODE) + message(STATUS "venv stdout: ${_VENV_OUTPUT}") + message(STATUS "venv stderr: ${_VENV_ERROR}") + message(FATAL_ERROR "Failed to create venv (exit code: ${_VENV_EXITCODE}). Ensure Python has venv module installed: python -m venv --help") + endif() + + if(DEFINED DD_VERSION) + execute_process( + COMMAND ${_VENV_PIP} install imas_data_dictionary==${DD_VERSION} + RESULT_VARIABLE _PIP_EXITCODE + OUTPUT_VARIABLE _PIP_OUTPUT + ERROR_VARIABLE _PIP_ERROR + ) + else() + execute_process( + COMMAND ${_VENV_PIP} install imas_data_dictionary + RESULT_VARIABLE _PIP_EXITCODE + OUTPUT_VARIABLE _PIP_OUTPUT + ERROR_VARIABLE _PIP_ERROR + ) + endif() + + if(_PIP_EXITCODE) + message(STATUS "imas_data_dictionary pip output: ${_PIP_OUTPUT}") + message(STATUS "imas_data_dictionary pip error: ${_PIP_ERROR}") + message(FATAL_ERROR "Failed to install imas_data_dictionary dependency (exit code: ${_PIP_EXITCODE}). Check network connectivity and Python wheel compatibility.") + endif() + + execute_process( + COMMAND ${_VENV_PIP} install saxonche + RESULT_VARIABLE _PIP_EXITCODE + OUTPUT_VARIABLE _PIP_OUTPUT + ERROR_VARIABLE _PIP_ERROR + ) + + if(_PIP_EXITCODE) + message(STATUS "saxonche pip output: ${_PIP_OUTPUT}") + message(STATUS "saxonche pip error: ${_PIP_ERROR}") + message(FATAL_ERROR "Failed to install saxonche dependency (exit code: ${_PIP_EXITCODE}). Check network connectivity and Python wheel compatibility.") + endif() + endif() +# Set up idsinfo command path +if(WIN32) + set(_IDSINFO_COMMAND "${CMAKE_CURRENT_BINARY_DIR}/dd_build_env/Scripts/idsinfo.exe") +else() + set(_IDSINFO_COMMAND "${CMAKE_CURRENT_BINARY_DIR}/dd_build_env/bin/idsinfo") +endif() + + # Use idsinfo idspath command from venv to get the path to IDSDef.xml or data_dictionary.xml execute_process( - COMMAND idsinfo idspath + COMMAND ${_IDSINFO_COMMAND} idspath OUTPUT_VARIABLE IDSDEF OUTPUT_STRIP_TRAILING_WHITESPACE RESULT_VARIABLE _IDSINFO_EXITCODE @@ -58,37 +135,115 @@ if( NOT AL_DOWNLOAD_DEPENDENCIES AND NOT AL_DEVELOPMENT_LAYOUT ) if( NOT DD_IDENTIFIER_FILES ) message( WARNING "No identifier XML files found in Data Dictionary at: ${IDSDEF}" ) endif() -else() - # Build the DD from source: - include(FetchContent) - - if( AL_DOWNLOAD_DEPENDENCIES ) - # Download the Data Dictionary from the ITER git: - FetchContent_Declare( - data-dictionary - GIT_REPOSITORY ${DD_GIT_REPOSITORY} - GIT_TAG ${DD_VERSION} + + # When using pre-installed DD, we still need venv for extracting IDS names and version + # Create Python venv and install saxonche if not already done + if(NOT EXISTS "${_VENV_PYTHON}") + execute_process( + COMMAND ${PYTHON_EXECUTABLE} -m venv dd_build_env + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + RESULT_VARIABLE _VENV_EXITCODE + OUTPUT_VARIABLE _VENV_OUTPUT + ERROR_VARIABLE _VENV_ERROR ) - else() - # Look in ../data-dictionary for the data dictionary - if( NOT( AL_PARENT_FOLDER ) ) - set( AL_PARENT_FOLDER ${CMAKE_CURRENT_SOURCE_DIR}/.. ) - endif() - set( DD_SOURCE_DIRECTORY ${AL_PARENT_FOLDER}/data-dictionary ) - if( NOT IS_DIRECTORY ${DD_SOURCE_DIRECTORY} ) - message( FATAL_ERROR - "${DD_SOURCE_DIRECTORY} does not exist. Please clone the " - "data-dictionary repository or set AL_DOWNLOAD_DEPENDENCIES=ON." - ) + + if(_VENV_EXITCODE) + message(STATUS "venv stdout: ${_VENV_OUTPUT}") + message(STATUS "venv stderr: ${_VENV_ERROR}") + message(FATAL_ERROR "Failed to create venv (exit code: ${_VENV_EXITCODE}). Ensure Python has venv module installed: python -m venv --help") endif() - FetchContent_Declare( - data-dictionary - SOURCE_DIR ${DD_SOURCE_DIRECTORY} + execute_process( + COMMAND ${_VENV_PIP} install saxonche + RESULT_VARIABLE _PIP_EXITCODE + OUTPUT_VARIABLE _PIP_OUTPUT + ERROR_VARIABLE _PIP_ERROR ) - set( DD_SOURCE_DIRECTORY ) # unset temporary var + + if(_PIP_EXITCODE) + message(STATUS "saxonche pip output: ${_PIP_OUTPUT}") + message(STATUS "saxonche pip error: ${_PIP_ERROR}") + message(FATAL_ERROR "Failed to install saxonche dependency (exit code: ${_PIP_EXITCODE}). Check network connectivity and Python wheel compatibility.") + endif() endif() - FetchContent_MakeAvailable( data-dictionary ) +else() + if(WIN32) + # Build the DD from source using direct git commands: + if( AL_DOWNLOAD_DEPENDENCIES ) + # Download the Data Dictionary from the ITER git: + set( data-dictionary_SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/_deps/data-dictionary-src" ) + if( NOT EXISTS "${data-dictionary_SOURCE_DIR}/.git" ) + message( STATUS "Cloning data-dictionary from ${DD_GIT_REPOSITORY}" ) + execute_process( + COMMAND git clone "${DD_GIT_REPOSITORY}" "${data-dictionary_SOURCE_DIR}" + RESULT_VARIABLE _GIT_CLONE_RESULT + ERROR_VARIABLE _GIT_CLONE_ERROR + ) + if( _GIT_CLONE_RESULT ) + message( FATAL_ERROR "Failed to clone data-dictionary: ${_GIT_CLONE_ERROR}" ) + endif() + endif() + # Checkout the specified version + execute_process( + COMMAND git fetch origin + WORKING_DIRECTORY "${data-dictionary_SOURCE_DIR}" + RESULT_VARIABLE _GIT_FETCH_RESULT + ) + execute_process( + COMMAND git checkout "${DD_VERSION}" + WORKING_DIRECTORY "${data-dictionary_SOURCE_DIR}" + RESULT_VARIABLE _GIT_CHECKOUT_RESULT + ERROR_VARIABLE _GIT_CHECKOUT_ERROR + ) + if( _GIT_CHECKOUT_RESULT ) + message( FATAL_ERROR "Failed to checkout ${DD_VERSION}: ${_GIT_CHECKOUT_ERROR}" ) + endif() + else() + # Look in ../data-dictionary for the data dictionary + if( NOT( AL_PARENT_FOLDER ) ) + set( AL_PARENT_FOLDER ${CMAKE_CURRENT_SOURCE_DIR}/.. ) + endif() + set( data-dictionary_SOURCE_DIR ${AL_PARENT_FOLDER}/data-dictionary ) + if( NOT IS_DIRECTORY ${data-dictionary_SOURCE_DIR} ) + message( FATAL_ERROR + "${data-dictionary_SOURCE_DIR} does not exist. Please clone the " + "data-dictionary repository or set AL_DOWNLOAD_DEPENDENCIES=ON." + ) + endif() + endif() + else() + # Build the DD from source: + include(FetchContent) + + if( AL_DOWNLOAD_DEPENDENCIES ) + # Download the Data Dictionary from the ITER git: + FetchContent_Declare( + data-dictionary + GIT_REPOSITORY ${DD_GIT_REPOSITORY} + GIT_TAG ${DD_VERSION} + ) + else() + # Look in ../data-dictionary for the data dictionary + if( NOT( AL_PARENT_FOLDER ) ) + set( AL_PARENT_FOLDER ${CMAKE_CURRENT_SOURCE_DIR}/.. ) + endif() + set( DD_SOURCE_DIRECTORY ${AL_PARENT_FOLDER}/data-dictionary ) + if( NOT IS_DIRECTORY ${DD_SOURCE_DIRECTORY} ) + message( FATAL_ERROR + "${DD_SOURCE_DIRECTORY} does not exist. Please clone the " + "data-dictionary repository or set AL_DOWNLOAD_DEPENDENCIES=ON." + ) + endif() + + FetchContent_Declare( + data-dictionary + SOURCE_DIR ${DD_SOURCE_DIRECTORY} + ) + set( DD_SOURCE_DIRECTORY ) # unset temporary var + endif() + FetchContent_MakeAvailable( data-dictionary ) + endif() + # get version of the data dictionary execute_process( @@ -108,33 +263,52 @@ else() endif() # We need the IDSDef.xml at configure time, ensure it is built - execute_process( - COMMAND ${Python_EXECUTABLE} -m venv dd_build_env - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - ) - - execute_process( - COMMAND ${CMAKE_CURRENT_BINARY_DIR}/dd_build_env/bin/pip install saxonche - RESULT_VARIABLE _PIP_EXITCODE - ) - - if(_PIP_EXITCODE) - message(FATAL_ERROR "Failed to install saxonche dependency") + # Create Python venv and install saxonche if not already done + if(NOT EXISTS "${_VENV_PYTHON}") + execute_process( + COMMAND ${PYTHON_EXECUTABLE} -m venv dd_build_env + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + RESULT_VARIABLE _VENV_EXITCODE + OUTPUT_VARIABLE _VENV_OUTPUT + ERROR_VARIABLE _VENV_ERROR + ) + + if(_VENV_EXITCODE) + message(STATUS "venv stdout: ${_VENV_OUTPUT}") + message(STATUS "venv stderr: ${_VENV_ERROR}") + message(FATAL_ERROR "Failed to create venv (exit code: ${_VENV_EXITCODE}). Ensure Python has venv module installed: python -m venv --help") + endif() + + execute_process( + COMMAND ${_VENV_PIP} install saxonche + RESULT_VARIABLE _PIP_EXITCODE + OUTPUT_VARIABLE _PIP_OUTPUT + ERROR_VARIABLE _PIP_ERROR + ) + + if(_PIP_EXITCODE) + message(STATUS "saxonche pip output: ${_PIP_OUTPUT}") + message(STATUS "saxonche pip error: ${_PIP_ERROR}") + message(FATAL_ERROR "Failed to install saxonche dependency (exit code: ${_PIP_EXITCODE}). Check network connectivity and Python wheel compatibility.") + endif() endif() execute_process( - COMMAND ${CMAKE_CURRENT_BINARY_DIR}/dd_build_env/bin/python "${al-core_SOURCE_DIR}/xsltproc.py" + COMMAND ${_VENV_PYTHON} "${al-common_SOURCE_DIR}/xsltproc.py" -xsl "dd_data_dictionary.xml.xsl" -o "IDSDef.xml" -s "dd_data_dictionary.xml.xsd" DD_GIT_DESCRIBE=${DD_GIT_DESCRIBE} WORKING_DIRECTORY ${data-dictionary_SOURCE_DIR} RESULT_VARIABLE _MAKE_DD_EXITCODE + OUTPUT_VARIABLE _MAKE_DD_OUTPUT + ERROR_VARIABLE _MAKE_DD_ERROR ) if( _MAKE_DD_EXITCODE ) - # make did not succeed: - message( FATAL_ERROR "Error while building the Data Dictionary. See output on previous lines." ) + message(STATUS "xsltproc.py output: ${_MAKE_DD_OUTPUT}") + message(STATUS "xsltproc.py error: ${_MAKE_DD_ERROR}") + message(FATAL_ERROR "Error while building the Data Dictionary (exit code: ${_MAKE_DD_EXITCODE}). Check paths and Saxon-HE configuration.") endif() # Populate IDSDEF filename @@ -149,19 +323,74 @@ else() endif() # Find out which IDSs exist and populate IDS_NAMES +# Ensure saxonche is installed before using xsltproc.py +# Check if saxonche is available in the venv +execute_process( + COMMAND ${_VENV_PYTHON} -c "import saxonche" + RESULT_VARIABLE _SAXONCHE_CHECK + OUTPUT_QUIET + ERROR_QUIET +) + +if(_SAXONCHE_CHECK) + message(STATUS "Installing saxonche in venv...") + execute_process( + COMMAND ${_VENV_PIP} install saxonche + RESULT_VARIABLE _PIP_EXITCODE + OUTPUT_VARIABLE _PIP_OUTPUT + ERROR_VARIABLE _PIP_ERROR + ) + + if(_PIP_EXITCODE) + message(STATUS "saxonche pip output: ${_PIP_OUTPUT}") + message(STATUS "saxonche pip error: ${_PIP_ERROR}") + message(FATAL_ERROR "Failed to install saxonche dependency (exit code: ${_PIP_EXITCODE}). Check network connectivity and Python wheel compatibility.") + endif() +endif() + set( list_idss_file ${al-common_SOURCE_DIR}/list_idss.xsl ) set( CMAKE_CONFIGURE_DEPENDS ${CMAKE_CONFIGURE_DEPENDS};${list_idss_file};${IDSDEF} ) +set( ids_names_tmpfile "${CMAKE_CURRENT_BINARY_DIR}/ids_names_tmp.txt" ) execute_process( COMMAND - ${LIBXSLT_XSLTPROC_EXECUTABLE} ${list_idss_file} ${IDSDEF} - OUTPUT_VARIABLE IDS_NAMES + ${_VENV_PYTHON} "${al-common_SOURCE_DIR}/xsltproc.py" + -xsl ${list_idss_file} + -s ${IDSDEF} + -o ${ids_names_tmpfile} + RESULT_VARIABLE _XSLT_RESULT + ERROR_VARIABLE _XSLT_ERROR ) +if(_XSLT_RESULT) + message(FATAL_ERROR "Failed to extract IDS names: ${_XSLT_ERROR}") +endif() +if(EXISTS ${ids_names_tmpfile}) + file(READ ${ids_names_tmpfile} IDS_NAMES) + string(STRIP "${IDS_NAMES}" IDS_NAMES) + file(REMOVE ${ids_names_tmpfile}) +else() + message(FATAL_ERROR "IDS names output file not created") +endif() set( list_idss_file ) # unset temporary var # DD version set( dd_version_file ${al-common_SOURCE_DIR}/dd_version.xsl ) +set( dd_version_tmpfile "${CMAKE_CURRENT_BINARY_DIR}/dd_version_tmp.txt" ) execute_process( COMMAND - ${LIBXSLT_XSLTPROC_EXECUTABLE} ${dd_version_file} ${IDSDEF} - OUTPUT_VARIABLE DD_VERSION + ${_VENV_PYTHON} "${al-common_SOURCE_DIR}/xsltproc.py" + -xsl ${dd_version_file} + -s ${IDSDEF} + -o ${dd_version_tmpfile} + RESULT_VARIABLE _XSLT_RESULT + ERROR_VARIABLE _XSLT_ERROR ) -string( REGEX REPLACE "[+-]" "_" DD_SAFE_VERSION ${DD_VERSION} ) +if(_XSLT_RESULT) + message(FATAL_ERROR "Failed to extract DD version: ${_XSLT_ERROR}") +endif() +if(EXISTS ${dd_version_tmpfile}) + file(READ ${dd_version_tmpfile} DD_VERSION) + string(STRIP "${DD_VERSION}" DD_VERSION) + file(REMOVE ${dd_version_tmpfile}) +else() + message(FATAL_ERROR "DD version output file not created") +endif() +string( REGEX REPLACE "[+-]" "_" DD_SAFE_VERSION "${DD_VERSION}" ) set( dd_version_file ) # unset temporary var diff --git a/common/cmake/ALCommonConfig.cmake b/common/cmake/ALCommonConfig.cmake index badee05b..2431a5d9 100644 --- a/common/cmake/ALCommonConfig.cmake +++ b/common/cmake/ALCommonConfig.cmake @@ -13,8 +13,8 @@ option( AL_PLUGINS "Enable plugin framework for tests and examples" OFF ) option( AL_HLI_DOCS "Build the Sphinx-based High Level Interface documentation" OFF ) option( AL_DOCS_ONLY "Don't build anything, except the Sphinx-based High Level Interface documentation" OFF ) -# Find Saxon XSLT processor -find_package( SaxonHE REQUIRED ) +# Saxon XSLT processor has been replaced with Python saxonche +# No longer need to find SaxonHE - saxonche is installed automatically via pip in virtual environments if( NOT AL_DOWNLOAD_DEPENDENCIES ) if( DEFINED ENV{AL_COMMON_PATH} ) diff --git a/common/cmake/ALCore.cmake b/common/cmake/ALCore.cmake index 9492263e..21c6bc74 100644 --- a/common/cmake/ALCore.cmake +++ b/common/cmake/ALCore.cmake @@ -11,69 +11,176 @@ if( NOT AL_DOWNLOAD_DEPENDENCIES AND NOT AL_DEVELOPMENT_LAYOUT ) # Stop processing return() endif() - -include(FetchContent) - -if( AL_DOWNLOAD_DEPENDENCIES ) - # Download the AL core from the ITER git: - FetchContent_Declare( - al-core - GIT_REPOSITORY ${AL_CORE_GIT_REPOSITORY} - GIT_TAG ${AL_CORE_VERSION} - ) -else() - # Look in ../al-core - set( AL_SOURCE_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../al-core ) - if( NOT IS_DIRECTORY ${AL_SOURCE_DIRECTORY} ) - # Repository used to be called "al-lowlevel", check this directory as well for - # backwards compatibility: - set( AL_SOURCE_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../al-lowlevel ) - if( NOT IS_DIRECTORY ${AL_SOURCE_DIRECTORY} ) - message( FATAL_ERROR - "${AL_SOURCE_DIRECTORY} does not exist. Please clone the " - "al-core repository or set AL_DOWNLOAD_DEPENDENCIES=ON." +if(WIN32) + if( AL_DOWNLOAD_DEPENDENCIES ) + # Download the AL core from the ITER git using direct git commands: + set( al-core_SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/_deps/al-core-src" ) + if( NOT EXISTS "${al-core_SOURCE_DIR}/.git" ) + message( STATUS "Cloning al-core from ${AL_CORE_GIT_REPOSITORY}" ) + execute_process( + COMMAND git clone "${AL_CORE_GIT_REPOSITORY}" "${al-core_SOURCE_DIR}" + RESULT_VARIABLE _GIT_CLONE_RESULT + ERROR_VARIABLE _GIT_CLONE_ERROR ) + if( _GIT_CLONE_RESULT ) + message( FATAL_ERROR "Failed to clone al-core: ${_GIT_CLONE_ERROR}" ) + endif() + endif() + # Checkout the specified version + execute_process( + COMMAND git fetch origin + WORKING_DIRECTORY "${al-core_SOURCE_DIR}" + RESULT_VARIABLE _GIT_FETCH_RESULT + ) + execute_process( + COMMAND git checkout "${AL_CORE_VERSION}" + WORKING_DIRECTORY "${al-core_SOURCE_DIR}" + RESULT_VARIABLE _GIT_CHECKOUT_RESULT + ERROR_VARIABLE _GIT_CHECKOUT_ERROR + ) + if( _GIT_CHECKOUT_RESULT ) + message( FATAL_ERROR "Failed to checkout ${AL_CORE_VERSION}: ${_GIT_CHECKOUT_ERROR}" ) + endif() + else() + # Look in ../al-core + set( al-core_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../al-core ) + if( NOT IS_DIRECTORY ${al-core_SOURCE_DIR} ) + # Repository used to be called "al-lowlevel", check this directory as well for + # backwards compatibility: + set( al-core_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../al-lowlevel ) + if( NOT IS_DIRECTORY ${al-core_SOURCE_DIR} ) + message( FATAL_ERROR + "${al-core_SOURCE_DIR} does not exist. Please clone the " + "al-core repository or set AL_DOWNLOAD_DEPENDENCIES=ON." + ) + endif() endif() endif() +else() + include(FetchContent) + + if( AL_DOWNLOAD_DEPENDENCIES ) + # Download the AL core from the ITER git: + FetchContent_Declare( + al-core + GIT_REPOSITORY ${AL_CORE_GIT_REPOSITORY} + GIT_TAG ${AL_CORE_VERSION} + ) + else() + # Look in ../al-core + set( AL_SOURCE_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../al-core ) + if( NOT IS_DIRECTORY ${AL_SOURCE_DIRECTORY} ) + # Repository used to be called "al-lowlevel", check this directory as well for + # backwards compatibility: + set( AL_SOURCE_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../al-lowlevel ) + if( NOT IS_DIRECTORY ${AL_SOURCE_DIRECTORY} ) + message( FATAL_ERROR + "${AL_SOURCE_DIRECTORY} does not exist. Please clone the " + "al-core repository or set AL_DOWNLOAD_DEPENDENCIES=ON." + ) + endif() + endif() - FetchContent_Declare( - al-core - SOURCE_DIR ${AL_SOURCE_DIRECTORY} - ) - set( AL_SOURCE_DIRECTORY ) # unset temporary var + FetchContent_Declare( + al-core + SOURCE_DIR ${AL_SOURCE_DIRECTORY} + ) + set( AL_SOURCE_DIRECTORY ) # unset temporary var + endif() endif() + # Don't load the AL core when only building documentation if( NOT AL_DOCS_ONLY ) - FetchContent_MakeAvailable( al-core ) + # Ensure vcpkg packages are found in the subdirectory + if(WIN32) + # On Windows, ensure vcpkg packages are available to the subdirectory + if(DEFINED VCPKG_INSTALLED_DIR AND DEFINED VCPKG_TARGET_TRIPLET) + # Add vcpkg installed directory to CMAKE_PREFIX_PATH for the subdirectory + set(CMAKE_PREFIX_PATH "${VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET};${CMAKE_PREFIX_PATH}") + # Pass vcpkg variables to subdirectory by setting them in parent scope + set(VCPKG_INSTALLED_DIR "${VCPKG_INSTALLED_DIR}" CACHE STRING "vcpkg installed dir" FORCE) + set(VCPKG_TARGET_TRIPLET "${VCPKG_TARGET_TRIPLET}" CACHE STRING "vcpkg triplet" FORCE) + message(STATUS "ALCore: Passing vcpkg paths to al-core subdirectory") + message(STATUS " VCPKG_INSTALLED_DIR: ${VCPKG_INSTALLED_DIR}") + message(STATUS " VCPKG_TARGET_TRIPLET: ${VCPKG_TARGET_TRIPLET}") + message(STATUS " CMAKE_PREFIX_PATH: ${CMAKE_PREFIX_PATH}") + endif() + add_subdirectory( ${al-core_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}/_deps/al-core-build ) + else() + FetchContent_MakeAvailable( al-core ) + endif() get_target_property( AL_CORE_VERSION al VERSION ) endif() if( ${AL_PLUGINS} ) - if( ${AL_DOWNLOAD_DEPENDENCIES} ) - # Download the AL plugins from the ITER git: - FetchContent_Declare( - al-plugins - GIT_REPOSITORY ${AL_PLUGINS_GIT_REPOSITORY} - GIT_TAG ${AL_PLUGINS_VERSION} - ) - else() - # Look in ../plugins - set( PLUGINS_SOURCE_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../al-plugins ) - if( NOT IS_DIRECTORY ${PLUGINS_SOURCE_DIRECTORY} ) - message( FATAL_ERROR - "${PLUGINS_SOURCE_DIRECTORY} does not exist. Please clone the " - "al-plugins repository or set AL_DOWNLOAD_DEPENDENCIES=ON." + if(WIN32) + if( ${AL_DOWNLOAD_DEPENDENCIES} ) + # Download the AL plugins from the ITER git using direct git commands: + set( al-plugins_SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/_deps/al-plugins-src" ) + if( NOT EXISTS "${al-plugins_SOURCE_DIR}/.git" ) + message( STATUS "Cloning al-plugins from ${AL_PLUGINS_GIT_REPOSITORY}" ) + execute_process( + COMMAND git clone "${AL_PLUGINS_GIT_REPOSITORY}" "${al-plugins_SOURCE_DIR}" + RESULT_VARIABLE _GIT_CLONE_RESULT + ERROR_VARIABLE _GIT_CLONE_ERROR + ) + if( _GIT_CLONE_RESULT ) + message( FATAL_ERROR "Failed to clone al-plugins: ${_GIT_CLONE_ERROR}" ) + endif() + endif() + # Checkout the specified version + execute_process( + COMMAND git fetch origin + WORKING_DIRECTORY "${al-plugins_SOURCE_DIR}" + RESULT_VARIABLE _GIT_FETCH_RESULT ) + execute_process( + COMMAND git checkout "${AL_PLUGINS_VERSION}" + WORKING_DIRECTORY "${al-plugins_SOURCE_DIR}" + RESULT_VARIABLE _GIT_CHECKOUT_RESULT + ERROR_VARIABLE _GIT_CHECKOUT_ERROR + ) + if( _GIT_CHECKOUT_RESULT ) + message( FATAL_ERROR "Failed to checkout ${AL_PLUGINS_VERSION}: ${_GIT_CHECKOUT_ERROR}" ) + endif() + else() + # Look in ../plugins + set( al-plugins_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../al-plugins ) + if( NOT IS_DIRECTORY ${al-plugins_SOURCE_DIR} ) + message( FATAL_ERROR + "${al-plugins_SOURCE_DIR} does not exist. Please clone the " + "al-plugins repository or set AL_DOWNLOAD_DEPENDENCIES=ON." + ) + endif() endif() - FetchContent_Declare( - al-plugins - SOURCE_DIR ${PLUGINS_SOURCE_DIRECTORY} - ) - set( PLUGINS_SOURCE_DIRECTORY ) # unset temporary var + else() + if( ${AL_DOWNLOAD_DEPENDENCIES} ) + # Download the AL plugins from the ITER git: + FetchContent_Declare( + al-plugins + GIT_REPOSITORY ${AL_PLUGINS_GIT_REPOSITORY} + GIT_TAG ${AL_PLUGINS_VERSION} + ) + else() + # Look in ../plugins + set( PLUGINS_SOURCE_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../al-plugins ) + if( NOT IS_DIRECTORY ${PLUGINS_SOURCE_DIRECTORY} ) + message( FATAL_ERROR + "${PLUGINS_SOURCE_DIRECTORY} does not exist. Please clone the " + "al-plugins repository or set AL_DOWNLOAD_DEPENDENCIES=ON." + ) + endif() + + FetchContent_Declare( + al-plugins + SOURCE_DIR ${PLUGINS_SOURCE_DIRECTORY} + ) + set( PLUGINS_SOURCE_DIRECTORY ) # unset temporary var + endif() + FetchContent_MakeAvailable( al-plugins ) endif() - FetchContent_MakeAvailable( al-plugins ) endif() if( AL_HLI_DOCS ) diff --git a/common/cmake/ALSetCompilerFlags.cmake b/common/cmake/ALSetCompilerFlags.cmake index 4bd88c3b..b821ffce 100644 --- a/common/cmake/ALSetCompilerFlags.cmake +++ b/common/cmake/ALSetCompilerFlags.cmake @@ -34,8 +34,8 @@ endif() if(NOT DEFINED CMAKE_CXX_STANDARD) set( CMAKE_CXX_STANDARD 17 ) endif() -if( CMAKE_CXX_COMPILER_ID STREQUAL "Intel" ) - # icpc options +if( CMAKE_CXX_COMPILER_ID STREQUAL "Intel" OR CMAKE_CXX_COMPILER_ID STREQUAL "IntelLLVM" ) + # icpc/icpx options string( APPEND CMAKE_CXX_FLAGS # " -Wall" ) diff --git a/common/cmake/FindPThreads4W.cmake b/common/cmake/FindPThreads4W.cmake new file mode 100644 index 00000000..4de1783b --- /dev/null +++ b/common/cmake/FindPThreads4W.cmake @@ -0,0 +1,70 @@ +# FindPThreads4W.cmake - Minimal version +# Find the PThreads-Win32 library + +message(STATUS "FindPThreads4W: CMAKE_PREFIX_PATH = ${CMAKE_PREFIX_PATH}") +message(STATUS "FindPThreads4W: VCPKG_INSTALLED_DIR = ${VCPKG_INSTALLED_DIR}") +message(STATUS "FindPThreads4W: VCPKG_TARGET_TRIPLET = ${VCPKG_TARGET_TRIPLET}") +message(STATUS "FindPThreads4W: CMAKE_CURRENT_SOURCE_DIR = ${CMAKE_CURRENT_SOURCE_DIR}") +message(STATUS "FindPThreads4W: CMAKE_CURRENT_BINARY_DIR = ${CMAKE_CURRENT_BINARY_DIR}") + +# Try to determine vcpkg installed directory from build directory structure +set(_POSSIBLE_VCPKG_PATHS "") +if(CMAKE_CURRENT_BINARY_DIR MATCHES "(.*/build)/") + set(_BUILD_DIR "${CMAKE_MATCH_1}") + list(APPEND _POSSIBLE_VCPKG_PATHS + "${_BUILD_DIR}/vcpkg_installed/x64-windows" + "${_BUILD_DIR}/vcpkg_installed/x86-windows" + ) + message(STATUS "FindPThreads4W: Detected build dir: ${_BUILD_DIR}") +endif() + +# Find include directory and library +find_path(PThreads4W_INCLUDE_DIR + NAMES pthread.h + HINTS + ${PThreads4W_DIR} + ${VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET} + ${CMAKE_PREFIX_PATH} + ${_POSSIBLE_VCPKG_PATHS} + PATH_SUFFIXES include +) + +find_library(PThreads4W_LIBRARY + NAMES pthreadVC3 pthreadVCE3 pthreadVSE3 pthread pthreads + HINTS + ${PThreads4W_DIR} + ${VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET} + ${CMAKE_PREFIX_PATH} + ${_POSSIBLE_VCPKG_PATHS} + PATH_SUFFIXES lib +) + +message(STATUS "FindPThreads4W: PThreads4W_INCLUDE_DIR = ${PThreads4W_INCLUDE_DIR}") +message(STATUS "FindPThreads4W: PThreads4W_LIBRARY = ${PThreads4W_LIBRARY}") + +# Use standard CMake handling +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(PThreads4W + REQUIRED_VARS PThreads4W_INCLUDE_DIR PThreads4W_LIBRARY +) + +if(PThreads4W_FOUND) + set(PThreads4W_INCLUDE_DIRS ${PThreads4W_INCLUDE_DIR}) + set(PThreads4W_LIBRARIES ${PThreads4W_LIBRARY}) + + # Create imported target + if(NOT TARGET PThreads4W::PThreads4W) + add_library(PThreads4W::PThreads4W UNKNOWN IMPORTED) + set_target_properties(PThreads4W::PThreads4W PROPERTIES + IMPORTED_LOCATION "${PThreads4W_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${PThreads4W_INCLUDE_DIR}" + ) + if(WIN32) + set_target_properties(PThreads4W::PThreads4W PROPERTIES + INTERFACE_LINK_LIBRARIES "ws2_32" + ) + endif(WIN32) + endif(NOT TARGET PThreads4W::PThreads4W) +endif(PThreads4W_FOUND) + +mark_as_advanced(PThreads4W_INCLUDE_DIR PThreads4W_LIBRARY) diff --git a/common/cmake/FindSaxonHE.cmake b/common/cmake/FindSaxonHE.cmake deleted file mode 100644 index af2b5d2f..00000000 --- a/common/cmake/FindSaxonHE.cmake +++ /dev/null @@ -1,56 +0,0 @@ -# Find Saxon-HE XSLT processor -# -# Sets the following variables -# - SaxonHE_FOUND -# - SaxonHE_CLASSPATH -# - SaxonHE_VERSION - -find_package( Java COMPONENTS Runtime ) -include( FindPackageHandleStandardArgs ) - -macro( TestSaxon CLASSPATH ) - execute_process( - COMMAND ${Java_JAVA_EXECUTABLE} -cp "${CLASSPATH}" net.sf.saxon.Transform -? - ERROR_VARIABLE _Saxon_OUTPUT - RESULT_VARIABLE _Saxon_RESULT - OUTPUT_QUIET - ) - if( _Saxon_RESULT EQUAL 0 ) - set( SaxonHE_CLASSPATH "${CLASSPATH}" CACHE STRING "Java classpath containing Saxon-HE" FORCE ) - if( _Saxon_OUTPUT MATCHES "Saxon(-HE)? ([^ ]*) from" ) - set( SaxonHE_VERSION ${CMAKE_MATCH_2} ) - else() - set( SaxonHE_VERSION "Unknown" ) - endif() - endif() -endmacro() - -if( Java_FOUND AND NOT SaxonHE_CLASSPATH ) - # Check if saxon is already on the classpath - TestSaxon( "$ENV{CLASSPATH}" ) - if( NOT SaxonHE_CLASSPATH ) - # Try to find Saxon in /usr/share/java: - find_file( SaxonHE_JAR - NAMES Saxon-HE.jar saxon-he.jar - PATHS - /usr/share/java/ - /usr/local/share/java/ - ) - if( SaxonHE_JAR ) - TestSaxon( "${SaxonHE_JAR}" ) - endif() - endif() -endif() - -if( SaxonHE_CLASSPATH ) - # Saxon found (or classpath set by user) - if( NOT SaxonHE_VERSION ) - TestSaxon( "${SaxonHE_CLASSPATH}" ) - endif() -endif() - -find_package_handle_standard_args( - SaxonHE - REQUIRED_VARS SaxonHE_CLASSPATH SaxonHE_VERSION - VERSION_VAR SaxonHE_VERSION -) diff --git a/common/doc_common/building_installing.rst b/common/doc_common/building_installing.rst index 07b2fe67..a5571fb3 100644 --- a/common/doc_common/building_installing.rst +++ b/common/doc_common/building_installing.rst @@ -7,6 +7,11 @@ Documentation for developers wishing to contribute to the Access Layer can be fo the :ref:`Access Layer development guide`. Please refer to that guide if you wish to set up a development environment. +.. note:: + + For Windows-specific installation instructions, please refer to the + :doc:`Windows Installation Guide `. + .. _`build prerequisites`: @@ -404,3 +409,4 @@ Troubleshooting **Problem:** ``Target Boost::log already has an imported location`` This problem is known to occur with the ``2020b`` toolchain on SDCC. Add the CMake configuration option ``-D Boost_NO_BOOST_CMAKE=ON`` to work around the problem. + diff --git a/common/doc_common/plugins_examples.rst b/common/doc_common/plugins_examples.rst index 281b5ce4..03bbf340 100644 --- a/common/doc_common/plugins_examples.rst +++ b/common/doc_common/plugins_examples.rst @@ -1,6 +1,11 @@ Plugins examples ================ +.. note:: + + The plugin examples referenced in this documentation are maintained in the al-plugins repository. + Please refer to: https://git.iter.org/projects/IMAS/repos/al-plugins/browse for the complete source code. + The ``debug`` plugin -------------------- @@ -9,8 +14,7 @@ In this first example, we want to display the value of the field ``ids_properties/version_put/access_layer`` for a given IDS during the execution of a ``get()`` operation. -The debug plugin is a C++ class named ``Debug_plugin``. `Figure 10`_ shows -the header code: +The debug plugin is a C++ class named ``Debug_plugin``. The header code shows: - The Debug_plugin class inherits from the access_layer_plugin plugin interface @@ -20,12 +24,11 @@ the header code: - The private attributes shot, dataobjectname and occurrence will be initialized during the initialization of the plugin -.. literalinclude:: ./plugins/debug_plugin.h - :caption: **Figure 10:** Header of the Debug_plugin class - :name: Figure 10 - :language: C++ +.. note:: + + The complete source code for the debug_plugin.h header is available in the al-plugins repository. -`Figure 10a`_ shows the plugin implementation code: +The plugin implementation code includes: - Plugin initialization occurs in the ``begin_global_action(...)`` function. However, no initialization is required in this example. @@ -42,19 +45,16 @@ the header code: ``getReadbackName(path, index)`` returns an empty string, meaning that the ``debug`` plugin does not define any *readback* plugin. +.. note:: -.. literalinclude:: ./plugins/debug_plugin.cpp - :caption: **Figure 10a:** C++ plugin implementation code - :name: Figure 10a - :language: C++ + The complete source code for the debug_plugin.cpp implementation is available in the al-plugins repository. Plugin compilation: creating a shared library ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -`Figure 11`_ shows the Makefile for compiling the plugin. This makefile -will also compile the C++ HLI code ``test_debug_plugin.cpp``, which uses -this plugin (`Figure 13`_). +The Makefile for compiling the plugin will also compile the C++ HLI code ``test_debug_plugin.cpp``, which uses +this plugin. Executing the Makefile generates the shared library ``debug_plugin.so`` and creates an executable ``test_debug_plugin`` for the HLI test @@ -336,25 +336,22 @@ Simplifying plugin code: introducing the ``AL_reader_helper_plugin`` class In this section, we show how to simplify the ``debug`` plugin code presented previously. For this purpose, we introduce the new helper class ``AL_reader_helper_plugin`` whose part of the header (provenance -feature operations have been removed for clarity) is shown in `Figure 15`_. -`Figure 16`_ depicts its implementation code. +feature operations have been removed for clarity) is shown in Figure 15. +Figure 16 depicts its implementation code. -.. literalinclude:: ./plugins/al_reader_helper_plugin.h - :caption: **Figure 15:** the ``AL_reader_helper_plugin`` class header - :name: Figure 15 - :language: C++ +.. note:: + + The complete source code for the al_reader_helper_plugin.h header is available in the al-plugins repository. By inheriting the helper class, we obtain the header of the ``Debug_plugin`` -class depicted in `Figure 17`_. The header code declares only the +class depicted in Figure 17. The header code declares only the ``read_data(..)`` function (from the plugin interface) whose implementation is overridden in the simplified implementation code of the ``Debug_plugin`` -class (`Figure 18`_). +class (Figure 18). +.. note:: -.. literalinclude:: ./plugins/al_reader_helper_plugin.cpp - :caption: **Figure 16:** the ``AL_reader_helper_plugin`` class implementation - :name: Figure 16 - :language: C++ + The complete source code for the al_reader_helper_plugin.cpp implementation is available in the al-plugins repository. .. code-block:: C++ :caption: **Figure 17:** the simplified ``Debug_plugin`` class header @@ -425,20 +422,13 @@ The requirement of IMAS-3121 ITER JIRA ticket is to fill in the ``ids_properties/creation_date`` node during a ``put()`` operation with the current date in the form YYYY-MM-DD. -.. literalinclude:: ./plugins/creation_date_plugin.cpp - :caption: **Figure 25:** Creation_date_plugin class implementation - :name: Figure 25 - :language: C++ +.. note:: -The ``Creation_date_plugin`` class whose implementation is depicted in -`Figure 25`_ implements this feature. `Figure 26`_ displays the header file -content. Provenance feature operations have been removed for clarity in -these files. + The complete source code for the creation_date_plugin.cpp implementation is available in the al-plugins repository. -.. literalinclude:: ./plugins/creation_date_plugin.cpp - :caption: **Figure 26:** Creation_date_plugin class header - :name: Figure 26 - :language: C++ +The ``Creation_date_plugin`` class implements this feature. The header file +content is also available in the al-plugins repository. Provenance feature operations have been removed for clarity in +these files. .. code-block:: python :caption: **Figure 27:** python client of the ``creation_date`` plugin @@ -789,21 +779,15 @@ Building a partial ``get()`` operation Skipping the read of an array of structure ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. literalinclude:: ./plugins/partial_get_plugin.cpp - :caption: **Figure 31:** the PartialGetPlugin class implementation - :name: Figure 31 - :language: C++ +.. note:: -.. literalinclude:: ./plugins/partial_get_plugin.h - :caption: **Figure 32:** the PartialGetPlugin header class - :name: Figure 32 - :language: C++ + The complete source code for the PartialGetPlugin class implementation (partial_get_plugin.cpp) + and header (partial_get_plugin.h) is available in the al-plugins repository. In a first use-case, the user wants to access only few attributes of the ``equilibrium`` IDS for many shots. In order to speed up reading, he decides to skip the loading of the ``grids_ggd`` array of structures (AOS). -The use of the ``PartialGetPlugin`` class implementation (`Figure 31`_) with -its header (`Figure 32`_) provides an efficient solution. During the ``get()`` +The ``PartialGetPlugin`` class provides an efficient solution. During the ``get()`` operation, the plugin intercepts the HLI call to the function ``al_begin_arraystruct_action(...)`` for the ``grids_ggd`` AOS and sets its size (using the arraySize pointer) to 0 after displaying a warning to the diff --git a/common/xsltproc.py b/common/xsltproc.py new file mode 100644 index 00000000..009b7ad4 --- /dev/null +++ b/common/xsltproc.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python +# simple net.sf.saxon.Transform cli replacement via saxonche Python bindings +# example invokation: +# ./xsltproc.py -xsl IDSDef2MDSpreTree.xsl -s IDSDef.xml -o output.xml DD_GIT_DESCRIBE=1 AL_GIT_DESCRIBE=1 + +import argparse +import logging + +import saxonche + + +def parse_arguments() -> tuple: + """Parse arguments, similar to net.sf.saxon.Transform...""" + + parser = argparse.ArgumentParser( + prog="xsltproc.py", + description="Imitates Saxon-HE's net.sf.saxon.Transform.", + epilog="Additional arguments in format key=value will be set as xml parameters", + ) + parser.add_argument( + "-xsl", + "--stylesheet_file", + type=str, + required=True, + help="XSL style sheet file", + ) + parser.add_argument( + "-s", + "--source_file", + type=str, + required=True, + help="source XML document", + ) + parser.add_argument( + "-o", + "--output_file", + type=str, + required=True, + help="transformed output XML document", + ) + + args, other_args = parser.parse_known_args() + # Convert list of strings "key=value" into dict(key=value, ...) + other_kwargs = {k: v for k, v in map(lambda x: x.split("="), other_args)} + return (args, other_kwargs) + + +def saxon_xsltproc( + source_file: str, stylesheet_file: str, output_file: str, **kwargs +) -> None: + with saxonche.PySaxonProcessor(license=False) as proc: + xsltproc = proc.new_xslt30_processor() + for key, value in kwargs.items(): + string_value = proc.make_string_value(value) + xsltproc.set_parameter(key, string_value) + xsltproc.transform_to_file( + source_file=source_file, + stylesheet_file=stylesheet_file, + output_file=output_file, + ) + + +if __name__ == "__main__": + logging.basicConfig(level=logging.INFO) + args, other_kwargs = parse_arguments() + saxon_xsltproc( + source_file=args.source_file, + stylesheet_file=args.stylesheet_file, + output_file=args.output_file, + **other_kwargs, + ) diff --git a/docs/requirements.txt b/docs/requirements.txt index e512142f..d700e884 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,5 +1,5 @@ sphinx-immaterial>=0.12.0 sphinx-tabs>=3.4.0 sphinx-design>=0.5.0 -sphinx>=5.0 +sphinx>=5.0,<9.0 numpy>=1.20.0 diff --git a/docs/source/user_guide/installation.rst b/docs/source/user_guide/installation.rst index 3b72bdd0..dc2f77bd 100644 --- a/docs/source/user_guide/installation.rst +++ b/docs/source/user_guide/installation.rst @@ -17,11 +17,7 @@ Requirements - **Python 3.8+** - **Linux** (fully supported) -- **macOS and Windows** (experimental - in testing) -.. note:: - macOS and Windows support is still being tested. Linux is the primary supported platform. - Please report any issues on `GitHub `_. Binary wheels are provided for all platforms, so you don't need to compile anything. @@ -51,6 +47,13 @@ To build the IMAS-Core you need: - Boost C++ libraries (1.66 or newer) - PkgConfig +On Windows +- **Visual Studio 2022** with: + - Desktop Development with C++ + - C++ Make Tools for Windows +- **CMake** (included with Visual Studio) + + The following dependencies are only required for some of the components: - Backends @@ -62,9 +65,7 @@ The following dependencies are only required for some of the components: .. [#uda_install] When installing UDA, make sure you have `Cap'n'Proto `__ installed in your system - and add its support by adding the CMake switch `-DENABLE_CAPNP=ON` when configuring UDA. - - + and add its support by adding the CMake switch `-DENABLE_CAPNP=ON` when configuring UDA. Standard environments: .. md-tab-set:: @@ -89,6 +90,23 @@ Standard environments: details. - MATLAB, which is not freely available. + .. md-tab-item:: Windows with Visual Studio + + First, set up vcpkg: + + .. code-block:: bash + + git clone https://github.com/microsoft/vcpkg.git + cd vcpkg # VCPKG_INSTALLATION_PATH + bootstrap-vcpkg.bat + + Then run these commands in PowerShell before building: + + .. code-block:: powershell + + $env:PATH += ";C:\Program Files\Microsoft Visual Studio\2022\Community\Common7\IDE\CommonExtensions\Microsoft\CMake\CMake\bin\" + $env:PATH += ";C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.44.35207\bin\HostX86\x86" + $env:PATH += ";" Building and installing IMAS-Core --------------------------------- @@ -107,18 +125,38 @@ First you need to clone the repository of the IMAS-Core you want to build: git clone git@github.com:iterorganization/IMAS-Core.git -cmake configuration +CMake configuration ~~~~~~~~~~~~~~~~~~~ Once you have cloned the repository, navigate your shell to the folder and run cmake. You can pass configuration options with ``-D OPTION=VALUE``. See below list for an overview of configuration options. +On Linux +'''''''' + .. code-block:: bash cd IMAS-Core cmake -B build -D CMAKE_INSTALL_PREFIX=$HOME/install -D OPTION1=VALUE1 -D OPTION2=VALUE2 [...] +On Windows +'''''''''' + +**Debug Build:** + +.. code-block:: bash + + cmake -Bbuild -S . -DVCPKG=ON -DCMAKE_INSTALL_PREFIX="" -DCMAKE_TOOLCHAIN_FILE="/scripts/buildsystems/vcpkg.cmake" -DAL_DOWNLOAD_DEPENDENCIES=ON cmake -B build -DCMAKE_INSTALL_PREFIX="$(pwd)/test-install/" -DAL_BACKEND_HDF5=ON -DAL_BACKEND_MDSPLUS=ON -DAL_BACKEND_UDA=ON -DAL_BUILD_MDSPLUS_MODELS=ON -DAL_PYTHON_BINDINGS=no-build-isolation -DAL_DOWNLOAD_DEPENDENCIES=ON -DDD_GIT_REPOSITORY=https://github.com/iterorganization/IMAS-Data-Dictionary.git -DDD_VERSION=main -DBoost_NO_BOOST_CMAKE=ON -DCMAKE_CXX_STANDARD=17 -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++ + +**Release Build:** + +.. code-block:: bash + + cmake -Bbuild -S . -DCMAKE_BUILD_TYPE=Release -DVCPKG=ON -DCMAKE_INSTALL_PREFIX="" -DCMAKE_TOOLCHAIN_FILE="/scripts/buildsystems/vcpkg.cmake" -DAL_DOWNLOAD_DEPENDENCIES=ON cmake -B build -DCMAKE_INSTALL_PREFIX="$(pwd)/test-install/" -DAL_BACKEND_HDF5=ON -DAL_BACKEND_MDSPLUS=ON -DAL_BACKEND_UDA=ON -DAL_BUILD_MDSPLUS_MODELS=ON -DAL_PYTHON_BINDINGS=no-build-isolation -DAL_DOWNLOAD_DEPENDENCIES=ON -DDD_GIT_REPOSITORY=https://github.com/iterorganization/IMAS-Data-Dictionary.git -DDD_VERSION=main -DBoost_NO_BOOST_CMAKE=ON -DCMAKE_CXX_STANDARD=17 -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++ + + + .. note:: CMake will automatically fetch dependencies from required repositories diff --git a/include/fix_include_windows.h b/include/fix_include_windows.h index 01692d40..db160e10 100644 --- a/include/fix_include_windows.h +++ b/include/fix_include_windows.h @@ -16,6 +16,9 @@ To fix std::min() after any #include "microsoft-mega-api.h" // use the Standard C++ std::min() and std::max() and ensure to #include #include + +#ifdef __cplusplus #include using std::max; -using std::min; \ No newline at end of file +using std::min; +#endif \ No newline at end of file diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index 71f8b780..528e33f6 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -26,6 +26,62 @@ else() set(LIBRARY_DIRS) endif() +# Add vcpkg directories on Windows +if(WIN32) + message(STATUS "Searching for vcpkg directories...") + message(STATUS " CMAKE_BINARY_DIR: ${CMAKE_BINARY_DIR}") + message(STATUS " CMAKE_TOOLCHAIN_FILE: ${CMAKE_TOOLCHAIN_FILE}") + message(STATUS " VCPKG_INSTALLED_DIR: ${VCPKG_INSTALLED_DIR}") + message(STATUS " VCPKG_TARGET_TRIPLET: ${VCPKG_TARGET_TRIPLET}") + + # Method 1: Try VCPKG_INSTALLED_DIR variable + if(DEFINED VCPKG_INSTALLED_DIR AND DEFINED VCPKG_TARGET_TRIPLET) + if(EXISTS "${VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/bin") + list(APPEND LIBRARY_DIRS "${VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/bin") + list(APPEND LIBRARY_DIRS "${VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/debug/bin") + message(STATUS " Found via VCPKG_INSTALLED_DIR: ${VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}") + endif() + endif() + + # Method 2: Try extracting from CMAKE_TOOLCHAIN_FILE + if(DEFINED CMAKE_TOOLCHAIN_FILE) + get_filename_component(VCPKG_ROOT "${CMAKE_TOOLCHAIN_FILE}" DIRECTORY) + get_filename_component(VCPKG_ROOT "${VCPKG_ROOT}" DIRECTORY) + get_filename_component(VCPKG_ROOT "${VCPKG_ROOT}" DIRECTORY) + if(EXISTS "${VCPKG_ROOT}/installed/x64-windows/bin") + list(APPEND LIBRARY_DIRS "${VCPKG_ROOT}/installed/x64-windows/bin") + list(APPEND LIBRARY_DIRS "${VCPKG_ROOT}/installed/x64-windows/debug/bin") + message(STATUS " Found via CMAKE_TOOLCHAIN_FILE: ${VCPKG_ROOT}/installed/x64-windows") + endif() + endif() + + # Method 3: Search parent directories for vcpkg_installed (FetchContent/ExternalProject builds) + set(SEARCH_DIR "${CMAKE_BINARY_DIR}") + foreach(i RANGE 6) + get_filename_component(SEARCH_DIR "${SEARCH_DIR}" DIRECTORY) + if(EXISTS "${SEARCH_DIR}/vcpkg_installed/x64-windows/bin") + list(APPEND LIBRARY_DIRS "${SEARCH_DIR}/vcpkg_installed/x64-windows/bin") + list(APPEND LIBRARY_DIRS "${SEARCH_DIR}/vcpkg_installed/x64-windows/debug/bin") + message(STATUS " Found via parent search: ${SEARCH_DIR}/vcpkg_installed") + break() + endif() + endforeach() + + # Remove duplicates + if(LIBRARY_DIRS) + list(REMOVE_DUPLICATES LIBRARY_DIRS) + endif() + + # Ensure we have at least some directories (install-time fallback) + if(NOT LIBRARY_DIRS) + message(WARNING "Could not find vcpkg directories at configure time. Adding PATH as fallback.") + # This will be resolved at install time from system PATH + list(APPEND LIBRARY_DIRS "$ENV{PATH}") + endif() + + message(STATUS "Python wheel LIBRARY_DIRS: ${LIBRARY_DIRS}") +endif() + # Build POST_EXCLUDE_REGEXES list based on platform set(POST_EXCLUDE_PATTERNS ".*system32/.*\\.dll" # Windows system DLLs diff --git a/src/hdf5/CMakeLists.txt b/src/hdf5/CMakeLists.txt index dc9c043b..9bc92385 100644 --- a/src/hdf5/CMakeLists.txt +++ b/src/hdf5/CMakeLists.txt @@ -1,6 +1,13 @@ # CMake configuration for the HDF5 backend - -find_package( HDF5 COMPONENTS C HL REQUIRED ) +if(WIN32) + # Try modern CONFIG mode first (vcpkg), fallback to legacy FindHDF5 module + find_package( hdf5 CONFIG QUIET ) + if( NOT hdf5_FOUND ) + find_package( HDF5 COMPONENTS C HL REQUIRED ) + endif() +else() + find_package( HDF5 COMPONENTS C HL REQUIRED ) +endif() target_sources( al PRIVATE hdf5_backend.cpp @@ -14,8 +21,23 @@ target_sources( al PRIVATE hdf5_backend_factory.cpp ) target_compile_definitions( al PRIVATE -DHDF5 ) - -target_include_directories( al PRIVATE ${HDF5_C_INCLUDE_DIRS} ${CMAKE_CURRENT_SOURCE_DIR} ) -target_link_libraries( al PRIVATE ${HDF5_C_LIBRARIES} ) -target_compile_definitions( al PRIVATE ${HDF5_C_DEFINITIONS} ) +if(WIN32) + # Use modern CMake targets if available (vcpkg), otherwise legacy variables + if( TARGET hdf5::hdf5-shared ) + target_link_libraries( al PRIVATE hdf5::hdf5-shared hdf5::hdf5_hl-shared ) + elseif( TARGET hdf5::hdf5-static ) + target_link_libraries( al PRIVATE hdf5::hdf5-static hdf5::hdf5_hl-static ) + else() + # Legacy FindHDF5 module + target_include_directories( al PRIVATE ${HDF5_C_INCLUDE_DIRS} ) + target_link_libraries( al PRIVATE ${HDF5_C_LIBRARIES} ) + target_compile_definitions( al PRIVATE ${HDF5_C_DEFINITIONS} ) + endif() + + target_include_directories( al PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ) +else() + target_include_directories( al PRIVATE ${HDF5_C_INCLUDE_DIRS} ${CMAKE_CURRENT_SOURCE_DIR} ) + target_link_libraries( al PRIVATE ${HDF5_C_LIBRARIES} ) + target_compile_definitions( al PRIVATE ${HDF5_C_DEFINITIONS} ) +endif()