diff --git a/docs/source/user_guide/installation.rst b/docs/source/user_guide/installation.rst index 3b72bdd0..a7b3081f 100644 --- a/docs/source/user_guide/installation.rst +++ b/docs/source/user_guide/installation.rst @@ -15,7 +15,7 @@ To install IMAS-Core via pip for Python users: Requirements ~~~~~~~~~~~~ -- **Python 3.8+** +- **Python 3.8+** - **Linux** (fully supported) - **macOS and Windows** (experimental - in testing) @@ -60,9 +60,9 @@ The following dependencies are only required for some of the components: - **UDA backend**: `UDA `__ libraries (2.7.5 or newer) [#uda_install]_ -.. [#uda_install] When installing UDA, make sure you have +.. [#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: @@ -119,10 +119,10 @@ overview of configuration options. cd IMAS-Core cmake -B build -D CMAKE_INSTALL_PREFIX=$HOME/install -D OPTION1=VALUE1 -D OPTION2=VALUE2 [...] -.. note:: +.. note:: CMake will automatically fetch dependencies from required repositories - for you. + for you. - `data-dictionary (git@github.com:iterorganization/IMAS-Data-Dictionary.git) `__ @@ -131,7 +131,7 @@ overview of configuration options. repository or to use a HTTPS URL instead of the default SSH URLs, you can update the :ref:`configuration options`. For example, add the following options to your ``cmake`` command to download the repositories over HTTPS instead of SSH: - + .. code-block:: text :caption: Use explicit options to download dependent repositories over HTTPS @@ -222,7 +222,7 @@ Configuration options When ``AL_DOWNLOAD_DEPENDENCIES`` is enabled, the following settings can be used to configure the location and/or version of the dependencies that should be used. - + - ``AL_CORE_GIT_REPOSITORY``, , ``DD_GIT_REPOSITORY``. Configure the git URLs where the ``IMAS-Core`` c.q. @@ -276,7 +276,7 @@ available configuration options, use the command-line tool ``ccmake`` or the gui Build the IMAS-Core ~~~~~~~~~~~~~~~~~~~ -Use ``cmake`` to build as shown below. +Use ``cmake`` to build as shown below. .. code-block:: bash @@ -359,9 +359,66 @@ can be passed using this method. The option names remain the same, but are prefi ``-C skbuild.cmake.define.``. +Optional: Build IMAS-Core with Meson +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can also build and install IMAS-Core using the Meson build system as an alternative to CMake. This can be useful for users who prefer Meson or want to try a different build workflow. + +First, ensure you have Meson and Ninja installed. You can install them with pip: + +.. code-block:: bash + + pip install meson ninja + +Then, clone the IMAS-Core repository if you haven't already: + +.. code-block:: bash + + git clone git@github.com:iterorganization/IMAS-Core.git + cd IMAS-Core + +Create a build directory and configure the project: + +.. code-block:: bash + + meson setup builddir + +You can pass Meson options at this step. For example, to enable or disable backends: + +.. code-block:: bash + + meson setup builddir -Dal_backend_hdf5=true -Dal_backend_uda=false + +To see all available options, run: + +.. code-block:: bash + + meson configure builddir + +Build the project: + +.. code-block:: bash + + meson compile -C builddir + +Install IMAS-Core (you may need sudo for system-wide installs): + +.. code-block:: bash + + meson install -C builddir + +You can also run tests using Meson: + +.. code-block:: bash + + meson test -C builddir + +For more information on Meson, see the official documentation: https://mesonbuild.com/ + + Optional: Test the IMAS-Core ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Following snippet shows how to run the test with pytest. Just ensure that you have +Following snippet shows how to run the test with pytest. Just ensure that you have AL_PYTHON_BINDINGS-ON in cmake configuration. If you set either of the options ``AL_EXAMPLES`` or ``AL_TESTS`` to ``ON``, you can run the corresponding test programs as follows: @@ -371,7 +428,7 @@ the corresponding test programs as follows: python3 -m venv build/pip_install source build/pip_install/bin/activate pip install --upgrade pip wheel - pip install numpy + pip install numpy set -x pip install --find-links=build/dist imas-core[test,cov] pytest --junitxml results.xml --cov imas_core --cov-report xml --cov-report html @@ -393,7 +450,7 @@ Access Layer. To help you with this, a file ``al_env.sh`` is installed. You can You may want to add this to your ``$HOME/.bashrc`` file to automatically make the IMAS-Core installation available for you. -.. note:: +.. note:: To use a ``public`` dataset, you also need to set the ``IMAS_HOME`` environment variable. For example, on SDCC, this would be ``export IMAS_HOME=/work/imas``. @@ -406,7 +463,7 @@ You may want to add this to your ``$HOME/.bashrc`` file to automatically make th Once you have set the required environment variables, you may continue Using the IMAS-Core. -If IMAS-Core is built with Python bindings, you can also use the Python bindings with imas-python. +If IMAS-Core is built with Python bindings, you can also use the Python bindings with imas-python. `imas_python `_. Just install with `pip install imas-python` and you can use the Python bindings. more information is here `imas-python `_. diff --git a/include/meson.build b/include/meson.build new file mode 100644 index 00000000..83f0132e --- /dev/null +++ b/include/meson.build @@ -0,0 +1,44 @@ +# ============================================================================= +# Configuration Data for al_defs.h +# ============================================================================= + +cdata = configuration_data() + +# Version information +cdata.set('PROJECT_VERSION', meson.project_version()) + +# Generate Config Headers +al_defs_h = configure_file( + input: 'al_defs.h.in', + output: 'al_defs.h', + configuration: cdata, +) + +install_headers(al_defs_h, subdir: 'al_core') + +# ============================================================================= +# Install Public Headers +# ============================================================================= + +install_headers( + 'access_layer_base_plugin.h', + 'access_layer_plugin.h', + 'access_layer_plugin_manager.h', + 'al_backend.h', + 'al_const.h', + 'al_context.h', + 'al_exception.h', + 'al_lowlevel.h', + 'data_interpolation.h', + 'extended_access_layer_plugin.h', + 'fix_include_windows.h', + 'provenance_plugin_feature.h', + 'readback_plugin_feature.h', + 'uri_parser.h', + subdir: 'al_core', +) + +# ============================================================================= +# Include Directories +# ============================================================================= +public_inc = include_directories('.') \ No newline at end of file diff --git a/meson.build b/meson.build new file mode 100644 index 00000000..bf48c1a2 --- /dev/null +++ b/meson.build @@ -0,0 +1,119 @@ +project( + 'imas-core', + 'cpp', + license: 'LGPL-3.0-or-later', + meson_version: '>=1.1.0', + version: run_command( + find_program('python3'), + '-m', 'setuptools_scm', + '--strip-dev', + check: true, + ).stdout().strip(), + default_options: [ + 'cpp_std=c++17', + 'buildtype=debugoptimized', + ], +) + +# ============================================================================= +# Compiler and System Setup +# ============================================================================= +cxx = meson.get_compiler('cpp') +host_system = host_machine.system() +host_cpu = host_machine.cpu_family() + +# ============================================================================= +# Warning Flags +# ============================================================================= +warning_flags = [] +if host_system == 'darwin' + warning_flags += [ + '-Wno-deprecated-declarations', + '-Wno-inconsistent-missing-override', + '-Wno-delete-non-abstract-non-virtual-dtor', + '-Wno-unused-but-set-variable', + '-Wno-unused-variable', + '-Wno-misleading-indentation', + '-Wno-reorder-ctor', + '-Wno-pessimizing-move', + '-Wno-sometimes-uninitialized', + ] +endif + +# ============================================================================= +# Access Layer core (al-core) Library +# ============================================================================= +if get_option('al_core') + subdir('include') + subdir('src') +else + # Use external al-core installation + al_core_dep = dependency('al-core') +endif + +# ============================================================================= +# Dummy Executable for version printing +# ============================================================================= +if get_option('al_dummy_exe') + imas_print_version = executable( + 'imas_print_version', + 'tests/imas_print_version.cpp', + dependencies: [al_core_dep], + install: true, + ) + test('imas_print_version_test', imas_print_version) +endif + +# ============================================================================= +# Test Low-level C API (al-core) +# ============================================================================= +if get_option('al_test') + # subdir('tests') # TODO: fix test sources +endif + +# ============================================================================= +# Python Bindings (imas-core) +# ============================================================================= +if get_option('python_bindings') + subdir('python') +endif + +# ============================================================================= +# MDSplus Models +# ============================================================================= +if get_option('mdsplus_models') + warning('MDSplus models are not yet supported in Meson build system.') + # subdir('models/mdsplus') +endif + +# ============================================================================= +# Summary +# ============================================================================= +if get_option('al_core') + summary( + { + 'HDF5': get_option('al_backend_hdf5'), + 'UDA': get_option('al_backend_uda'), + 'MDSplus': get_option('al_backend_mdsplus'), + }, + section: 'Backends', + ) + summary( + { + 'Core Arguments': core_args, + 'Warning Flags': warning_flags, + }, + section: 'Compiler Arguments', + ) +endif + +if get_option('python_bindings') + summary( + { + 'Python Version': py.language_version(), + 'Buildtime Python Path': py.full_path(), + 'Cython Arguments': cython_args, + }, + section: 'Python Bindings', + ) +endif \ No newline at end of file diff --git a/meson_options.txt b/meson_options.txt new file mode 100644 index 00000000..90eafad3 --- /dev/null +++ b/meson_options.txt @@ -0,0 +1,70 @@ +# ============================================================================= +# Libraries to build +# ============================================================================= + +option( + 'al_core', + type: 'boolean', + value: true, + description: 'Build internal core library, or use external installation', +) + +option( + 'al_dummy_exe', + type: 'boolean', + value: false, + description: 'Build dummy executable that prints version information.', +) + +option( + 'al_test', + type: 'boolean', + value: false, + description: 'Build tests for al-core library', +) + +option( + 'python_bindings', + type: 'boolean', + value: true, + description: 'Build Python wrapper', +) + +option( + 'mdsplus_models', + type: 'boolean', + value: false, + description: 'Build MDSplus models (requires MDSplus installation)', +) + +# ============================================================================= +# Backends Options +# ============================================================================= + +option( + 'al_backend_hdf5', + type: 'boolean', + value: true, + description: 'Enable HDF5 backend', +) + +option( + 'al_backend_mdsplus', + type: 'boolean', + value: false, + description: 'Enable MDSplus backend', +) + +option( + 'al_backend_uda', + type: 'boolean', + value: true, + description: 'Enable UDA backend', +) + +option( + 'uda_fat', + type: 'boolean', + value: false, + description: 'Use UDA FAT client libraries', +) \ No newline at end of file diff --git a/python/_version.py.in b/python/_version.py.in new file mode 100644 index 00000000..de6f4d49 --- /dev/null +++ b/python/_version.py.in @@ -0,0 +1,31 @@ +__all__ = [ + "__version__", + "__version_tuple__", + "version", + "version_tuple", + "__commit_id__", + "commit_id", +] + +TYPE_CHECKING = False +if TYPE_CHECKING: + from typing import Tuple + from typing import Union + + VERSION_TUPLE = Tuple[Union[int, str], ...] + COMMIT_ID = Union[str, None] +else: + VERSION_TUPLE = object + COMMIT_ID = object + +version: str +__version__: str +__version_tuple__: VERSION_TUPLE +version_tuple: VERSION_TUPLE +commit_id: COMMIT_ID +__commit_id__: COMMIT_ID + +__version__ = version = "@VERSION@" +__version_tuple__ = version_tuple = @VERSION_TUPLE@ + +__commit_id__ = commit_id = "@COMMIT_ID@" diff --git a/python/meson.build b/python/meson.build new file mode 100644 index 00000000..0b1a4388 --- /dev/null +++ b/python/meson.build @@ -0,0 +1,105 @@ +add_languages('cython', native: false) + +# ============================================================================= +# Version File +# ============================================================================= +# Get version using setuptools-scm +version = run_command( + find_program('python3'), + '-m', 'setuptools_scm', + check: true, +).stdout().strip() + +# Create tuple: split version like "5.5.2", ignoring dirty suffix etc. +split_ver = version.split('.') +ver_tuple = '(@0@, @1@, @2@)'.format( + split_ver[0], + split_ver[1], + split_ver[2], +) + +# Get commit id +git_commit = run_command( + find_program('git'), + 'rev-parse', + '--short', 'HEAD', + check: false, +).stdout().strip() + +if git_commit == '' + git_commit = 'None' +endif + +# Generate _version.py +version_file = configure_file( + input: '_version.py.in', + output: '_version.py', + configuration: { + 'VERSION': version, + 'VERSION_TUPLE': ver_tuple, + 'COMMIT_ID': git_commit, + }, +) + +# ============================================================================= +# Modules and Dependencies +# ============================================================================= +fs = import('fs') +py = import('python').find_installation(pure: false) +py_dep = py.dependency() +numpy_dep = dependency('numpy', required: false) + +if not numpy_dep.found() + # Get numpy include directory and library using Python + _incdic_numpy_abs = run_command( + py, + '-c', 'import numpy; print(numpy.get_include())', + check: true, + ).stdout().strip() + + # Create numpy dependency using include directory + numpy_dep = declare_dependency( + include_directories: include_directories(_incdic_numpy_abs), + ) +endif + +cython_args = ['--annotate'] + +# ============================================================================= +# Source files +# ============================================================================= +py_files = files( + 'imas_core/__init__.py', + 'imas_core/exception.py', + 'imas_core/imasdef.py', +) +pyx_files = files( + 'imas_core/_al_lowlevel.pyx', + 'imas_core/al_defs.pyx', +) +pxd_files = files( + 'imas_core/al_defs.pxd', + 'imas_core/al_lowlevel_interface.pxd', +) + +# ============================================================================= +# Build Python Extension Module +# ============================================================================= +# compile cython +foreach pyx_file : pyx_files + py.extension_module( + fs.stem(pyx_file), + pyx_file, + cython_args: cython_args, + dependencies: [al_core_dep, py_dep, numpy_dep], + install: true, + subdir: 'imas_core', + override_options: ['cython_language=cpp'], + ) +endforeach + +# Install pure python files +py.install_sources( + py_files + pxd_files + version_file, + subdir: 'imas_core', +) diff --git a/src/hdf5/meson.build b/src/hdf5/meson.build new file mode 100644 index 00000000..4b659401 --- /dev/null +++ b/src/hdf5/meson.build @@ -0,0 +1,27 @@ +# HDF5 Backend + +# Add sources +core_sources += files( + 'hdf5_backend.cpp', + 'hdf5_backend_factory.cpp', + 'hdf5_dataset_handler.cpp', + 'hdf5_events_handler.cpp', + 'hdf5_hs_selection_reader.cpp', + 'hdf5_hs_selection_writer.cpp', + 'hdf5_reader.cpp', + 'hdf5_utils.cpp', + 'hdf5_writer.cpp', +) + +# Add include directories +core_inc += include_directories('.') + +# Add dependency +core_deps += dependency('hdf5', language: 'cpp') + +# Add compiler flags +core_args += '-DHDF5' + +if host_system == 'windows' + core_args += '-DH5_BUILT_AS_DYNAMIC_LIB' +endif \ No newline at end of file diff --git a/src/mdsplus/meson.build b/src/mdsplus/meson.build new file mode 100644 index 00000000..3c5a88de --- /dev/null +++ b/src/mdsplus/meson.build @@ -0,0 +1,19 @@ +# MDSplus Backend + +# Add sources +core_sources += files( + 'mdsplus_backend.cpp', +) + +# Add include directories +core_inc += include_directories('.') + +# Add dependency +core_deps += dependency('mdsplus') + +# Add compiler flags +core_args += '-DMDSPLUS' + +if host_system == 'windows' + core_args += '-DMDSOBJECTSCPPSHRVS_EXPORTS' +endif \ No newline at end of file diff --git a/src/meson.build b/src/meson.build new file mode 100644 index 00000000..d7a19b44 --- /dev/null +++ b/src/meson.build @@ -0,0 +1,92 @@ +# AL-Core Library + +core_sources = files( + 'access_layer_plugin_manager.cpp', + 'al_backend.cpp', + 'al_const.cpp', + 'al_context.cpp', + 'al_exception.cpp', + 'al_lowlevel.cpp', + 'ascii_backend.cpp', + 'data_interpolation.cpp', + 'flexbuffers_backend.cpp', + 'memory_backend.cpp', + 'no_backend.cpp', +) + +# Enable ASCII backend +core_args = [ + '-DASCII', +] + +# Include local and public headers +core_inc = [include_directories('.'), public_inc] + +# ============================================================================= +# Dependencies +# ============================================================================= +boost_dep = dependency( + 'boost', + modules: ['filesystem'], + required: false, +) +if not boost_dep.found() + boost_dep = cxx.find_library('boost_filesystem') +endif + +core_deps = [ + boost_dep, +] + +if host_system == 'windows' + core_deps += [ + cxx.find_library('pthreads'), + dependency('dlfcn-win32', modules: ['dlfcn-win32::dl'], method: 'cmake'), + ] +endif + +# ============================================================================= +# Add Optional Backends +# ============================================================================= +if get_option('al_backend_hdf5') + subdir('hdf5') +endif + +if get_option('al_backend_mdsplus') + subdir('mdsplus') +endif + +if get_option('al_backend_uda') + subdir('uda') +endif + +# ============================================================================= +# Library +# ============================================================================= +al_core = library( + 'al', + core_sources, + cpp_args: core_args + warning_flags, + include_directories: core_inc, + dependencies: core_deps, + install: true, +) + +# Declare dependency for other libraries to use +al_core_dep = declare_dependency( + link_with: al_core, + include_directories: public_inc, +) + +# ============================================================================= +# Package Configuration +# ============================================================================= +pkg = import('pkgconfig') +pkg.generate( + libraries: al_core, + subdirs: ['al_core'], + name: 'al-core', + description: 'IMAS Access Layer core libraries for C', + url: 'https://github.com/iterorganization/IMAS-Core', + version: meson.project_version(), +) \ No newline at end of file diff --git a/src/uda/meson.build b/src/uda/meson.build new file mode 100644 index 00000000..dbead0d8 --- /dev/null +++ b/src/uda/meson.build @@ -0,0 +1,43 @@ +# UDA Backend + +# Add sources +core_sources += files( + 'pugixml.cpp', + 'uda_backend.cpp', + 'uda_cache.cpp', + 'uda_path.cpp', + 'uda_xml.cpp', +) + +# Add include directories +core_inc += include_directories('.') + +# Add dependencies +if get_option('uda_fat') + uda_fat_cpp_dep = dependency('uda-fat-cpp') + core_deps += [ + dependency('uda-fat-client'), + uda_fat_cpp_dep, + ] +else + uda_cpp_dep = dependency('uda-cpp') + core_deps += [ + dependency('uda-client'), + uda_cpp_dep, + ] +endif + +# Add compiler flags +if get_option('uda_fat') + if uda_fat_cpp_dep.version().version_compare('>=2.7.6') + core_args += ['-DUDA', '-DUDA_LEGACY_276', '-DUDA_CLIENT_FLAGS_API'] + else + core_args += ['-DUDA'] + endif +else + if uda_cpp_dep.version().version_compare('>=2.7.6') + core_args += ['-DUDA', '-DUDA_LEGACY_276', '-DUDA_CLIENT_FLAGS_API'] + else + core_args += ['-DUDA'] + endif +endif \ No newline at end of file diff --git a/tests/meson.build b/tests/meson.build new file mode 100644 index 00000000..83499a81 --- /dev/null +++ b/tests/meson.build @@ -0,0 +1,28 @@ +# Test executables for AL-Core + +# ============================================================================= +# Test executables +# ============================================================================= + +# C++ test executable +testlowlevel = executable( + 'testlowlevel', + 'testlowlevel.cpp', + dependencies: [al_core_dep], + cpp_args: ['-DNOIMPLIB'], + install: true, +) + +# C test executable +add_languages('c', native: false) +testlowlevel_c = executable( + 'testlowlevel_c', + 'testlowlevel_c.c', + dependencies: [al_core_dep], + c_args: ['-DNOIMPLIB'], + install: true, +) + +# Test cases +test('Test lowlevel C++', testlowlevel) +test('Test lowlevel C', testlowlevel_c) \ No newline at end of file