diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index fd641fed5c5002833601fcc86d78172560cf26f8..b5690ac4f9168c47c076a79c647b59d85dc9130e 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,6 +1,6 @@
# Compilation of scord and execution of tests
-image: gekkofs/scord:0.1.0
+image: bscstorage/scord:0.1.0
stages:
- build
@@ -39,4 +39,3 @@ test:
- pkill -9 scord
cache:
key: $CI_COMMIT_REF_SLUG
-
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5f461309ce9409800a219fa1679369ab9f178b7b..e9d0f67616b5fedc228324976b6cd6031186eb06 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -38,21 +38,21 @@ project(
# Set default build type and also populate a list of available options
get_property(is_multi_config GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
-if(NOT is_multi_config)
+if (NOT is_multi_config)
set(default_build_type "Release")
set(allowed_build_types Debug Release MinSizeRel RelWithDebInfo)
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "${allowed_build_types}")
- if(NOT CMAKE_BUILD_TYPE)
+ if (NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE
- "${default_build_type}"
- CACHE STRING "Choose the type of build." FORCE
- )
- elseif(NOT CMAKE_BUILD_TYPE IN_LIST allowed_build_types)
+ "${default_build_type}"
+ CACHE STRING "Choose the type of build." FORCE
+ )
+ elseif (NOT CMAKE_BUILD_TYPE IN_LIST allowed_build_types)
message(FATAL_ERROR "Unknown build type: ${CMAKE_BUILD_TYPE}")
- endif()
-endif()
+ endif ()
+endif ()
# make sure that debug versions for targets are used (if provided) in Debug mode
set_property(GLOBAL APPEND PROPERTY DEBUG_CONFIGURATIONS Debug)
@@ -83,10 +83,10 @@ include(scord-utils)
### transport library
set(SCORD_TRANSPORT_LIBRARY
- "libfabric"
- CACHE STRING
- "Transport library used by ${PROJECT_NAME} (default: libfabric)"
-)
+ "libfabric"
+ CACHE STRING
+ "Transport library used by ${PROJECT_NAME} (default: libfabric)"
+ )
set_property(CACHE SCORD_TRANSPORT_LIBRARY PROPERTY STRINGS libfabric ucx)
message(
STATUS "[${PROJECT_NAME}] Transport library: ${SCORD_TRANSPORT_LIBRARY}"
@@ -94,31 +94,31 @@ message(
### server transport protocol
set(SCORD_TRANSPORT_PROTOCOL
- "tcp"
- CACHE
- STRING
- "Change the default transport protocol for the ${PROJECT_NAME} server (default: tcp)"
-)
+ "tcp"
+ CACHE
+ STRING
+ "Change the default transport protocol for the ${PROJECT_NAME} server (default: tcp)"
+ )
message(
STATUS
- "[${PROJECT_NAME}] server default transport protocol: ${SCORD_TRANSPORT_PROTOCOL}"
+ "[${PROJECT_NAME}] server default transport protocol: ${SCORD_TRANSPORT_PROTOCOL}"
)
### server bind address
set(SCORD_BIND_ADDRESS
- "127.0.0.1"
- CACHE
- STRING
- "Define the bind address for the ${PROJECT_NAME} server (default: 127.0.0.1)"
-)
+ "127.0.0.1"
+ CACHE
+ STRING
+ "Define the bind address for the ${PROJECT_NAME} server (default: 127.0.0.1)"
+ )
message(STATUS "[${PROJECT_NAME}] server bind address: ${SCORD_BIND_ADDRESS}")
### server bind port
set(SCORD_BIND_PORT
- "52000"
- CACHE STRING
- "Define the bind port for the ${PROJECT_NAME} server (default: 52000)"
-)
+ "52000"
+ CACHE STRING
+ "Define the bind port for the ${PROJECT_NAME} server (default: 52000)"
+ )
message(STATUS "[${PROJECT_NAME}] server bind port: ${SCORD_BIND_PORT}")
option(SCORD_BUILD_EXAMPLES "Build examples (disabled by default)" OFF)
@@ -137,20 +137,16 @@ find_package(PkgConfig REQUIRED)
message(STATUS "[${PROJECT_NAME}] Checking for boost libraries")
find_package(Boost 1.53 REQUIRED COMPONENTS program_options)
-### yaml-cpp: required for reading configuration files
-message(STATUS "[${PROJECT_NAME}] Checking for yaml-cpp")
-find_package(YAMLCpp 0.6.2 REQUIRED)
-
### transport library
-if(SCORD_TRANSPORT_LIBRARY STREQUAL libfabric)
+if (SCORD_TRANSPORT_LIBRARY STREQUAL libfabric)
pkg_check_modules(libfabric REQUIRED IMPORTED_TARGET GLOBAL libfabric)
add_library(transport_library ALIAS PkgConfig::libfabric)
-elseif(SCORD_TRANSPORT_LIBRARY STREQUAL ucx)
+elseif (SCORD_TRANSPORT_LIBRARY STREQUAL ucx)
pkg_check_modules(ucx REQUIRED IMPORTED_TARGET GLOBAL ucx)
add_library(transport_library ALIAS PkgConfig::ucx)
-else()
+else ()
message(FATAL_ERROR "Unknown transport library: ${SCORD_TRANSPORT_LIBRARY}")
-endif()
+endif ()
### Mercury
message(STATUS "[${PROJECT_NAME}] Checking for Mercury")
@@ -188,6 +184,31 @@ FetchContent_Declare(
FetchContent_MakeAvailable(spdlog)
+### file_options: required for reading configuration files
+message(STATUS "[${PROJECT_NAME}] Downloading and building file_options")
+FetchContent_Declare(
+ file_options
+ GIT_REPOSITORY https://storage.bsc.es/gitlab/utils/file_options
+ GIT_TAG b2de92b3755e3391595bb368573b82abed16978d # v0.1.0-pre
+ GIT_SHALLOW ON
+ GIT_PROGRESS ON
+)
+
+FetchContent_MakeAvailable(file_options)
+
+### genopts: required for generating file_options schemas
+message(STATUS "[${PROJECT_NAME}] Downloading and building genopts")
+FetchContent_Declare(
+ genopts
+ GIT_REPOSITORY https://storage.bsc.es/gitlab/utils/genopts
+ GIT_TAG 1dcef400f8fbc6e1969c856ca844707b730c3002 # v0.1.0-pre
+ GIT_SHALLOW ON
+ GIT_PROGRESS ON
+)
+
+FetchContent_MakeAvailable(genopts)
+
+
### Mark any CMake variables imported from {fmt} and spdlog as advanced, so
### that they don't appear in cmake-gui or ccmake. Similarly for FETCHCONTENT
### variables.
@@ -199,6 +220,6 @@ mark_variables_as_advanced(REGEX "^(FETCHCONTENT|fmt|FMT|spdlog|SPDLOG)_.*$")
add_subdirectory(etc)
add_subdirectory(src)
-if(SCORD_BUILD_EXAMPLES)
+if (SCORD_BUILD_EXAMPLES)
add_subdirectory(examples)
-endif()
+endif ()
diff --git a/src/config/file_options/file_options.hpp b/COPYRIGHT_NOTICE
similarity index 78%
rename from src/config/file_options/file_options.hpp
rename to COPYRIGHT_NOTICE
index c4260d236bf013be23af9f02f937297d330f05f7..a3f50b7b97179c88f3237bc2f5360c6647cde940 100644
--- a/src/config/file_options/file_options.hpp
+++ b/COPYRIGHT_NOTICE
@@ -1,5 +1,5 @@
/******************************************************************************
- * Copyright 2021, Barcelona Supercomputing Center (BSC), Spain
+ * Copyright 2021-2022, Barcelona Supercomputing Center (BSC), Spain
*
* This software was partially supported by the EuroHPC-funded project ADMIRE
* (Project ID: 956748, https://www.admire-eurohpc.eu).
@@ -21,12 +21,3 @@
*
* SPDX-License-Identifier: GPL-3.0-or-later
*****************************************************************************/
-
-#ifndef SCORD_CONFIG_FILE_OPTIONS_HPP
-#define SCORD_CONFIG_FILE_OPTIONS_HPP
-
-#include "schema.hpp"
-#include "options_description.hpp"
-#include "yaml_parser.hpp"
-
-#endif /* SCORD_CONFIG_FILE_OPTIONS_HPP */
diff --git a/docker/0.1.0/Dockerfile b/docker/0.1.0/Dockerfile
index e7eb72b026a7fddecc5c02eaae25c1b02cb1e78e..65a59ad9fe8e738ec482e1734c17d59426d78345 100644
--- a/docker/0.1.0/Dockerfile
+++ b/docker/0.1.0/Dockerfile
@@ -7,28 +7,30 @@ ENV INSTALL_PATH /usr/local
RUN apt-get update && \
apt-get install -y --no-install-recommends \
- git \
- curl \
- ca-certificates \
- libtool \
- pkg-config \
- make \
- automake \
- gcc \
- g++ \
- procps \
- # AGIOS dependencies
- libconfig-dev \
- # Mercury dependencies
- libltdl-dev \
- lbzip2 \
+ git \
+ curl \
+ ca-certificates \
+ libtool \
+ pkg-config \
+ make \
+ automake \
+ gcc \
+ g++ \
+ procps \
+ # AGIOS dependencies
+ libconfig-dev \
+ # Mercury dependencies
+ libltdl-dev \
+ lbzip2 \
# Margo dependencies \
libjson-c-dev \
- # GekkoFS dependencies
- libboost-program-options-dev \
- uuid-dev \
+ # GekkoFS dependencies
+ libboost-program-options-dev \
+ uuid-dev \
python3 \
- libyaml-dev libcurl4-openssl-dev procps && \
+ libyaml-dev libcurl4-openssl-dev procps \
+ # genopts dependencies
+ python3-venv && \
# install cmake 3.14 since it's needed for some dependencies
curl -OL https://github.com/Kitware/CMake/releases/download/v3.23.1/cmake-3.23.1-Linux-x86_64.sh && \
chmod u+x ./cmake-3.23.1-Linux-x86_64.sh && \
diff --git a/src/config/CMakeLists.txt b/src/config/CMakeLists.txt
index ca9281a5c02505f7077c0490b445f79c4bd58dd9..e1e08003259f55591c923337fd7cbee9e75b27b2 100644
--- a/src/config/CMakeLists.txt
+++ b/src/config/CMakeLists.txt
@@ -22,30 +22,66 @@
# SPDX-License-Identifier: GPL-3.0-or-later #
################################################################################
+# Create a config target for all configuration code
add_library(config STATIC)
+# Since some of the sources will be auto-generated, we need to search for
+# includes in ${CMAKE_CURRENT_BINARY_DIR}
target_include_directories(
config PRIVATE ${CMAKE_SOURCE_DIR}/src ${CMAKE_CURRENT_SOURCE_DIR}
+ ${CMAKE_CURRENT_BINARY_DIR}
)
set_property(TARGET config PROPERTY POSITION_INDEPENDENT_CODE ON)
-configure_file(defaults.cpp.in defaults.cpp)
-
target_sources(
config
PRIVATE config.hpp
- config_schema.hpp
defaults.hpp
${CMAKE_CURRENT_BINARY_DIR}/defaults.cpp
- file_options/file_options.hpp
- file_options/schema.hpp
- file_options/options_description.hpp
- file_options/yaml_parser.hpp
- keywords.hpp
+ ${CMAKE_CURRENT_BINARY_DIR}/config_options.hpp
+ ${CMAKE_CURRENT_BINARY_DIR}/keywords.hpp
parsers.cpp
parsers.hpp
settings.cpp
settings.hpp
)
-target_link_libraries(config PRIVATE utils YAMLCpp::YAMLCpp)
+target_link_libraries(config PRIVATE utils file_options::file_options)
+
+# ##############################################################################
+# Produce several auto-generated files for 'config'
+# ##############################################################################
+
+# Default values for options
+configure_file(defaults.cpp.in defaults.cpp)
+
+# Automatic generation of file_options schemas. To facilitate the management of
+# any file-based configuration options, we generate a C++ schema from a YAML
+# file that allows the 'file_options' library to parse them fromm a
+# configuration file. The process works as follows:
+# 1. We define the desired options in 'file_options.yml'
+# 2. We rely on the 'genopts' tool to generate valid C++ schemas for the
+# 'file_options' library. This tool can be configured using a 'genopts.yml'
+# configuration file.
+# 3. In the daemon code, we use the facilities provided by the 'file_options'
+# library to access the options values.
+
+# Since the configuration in genopts.yml can be path-dependant, we rely on
+# CMake to substitute any special @variables@ for their actual values
+configure_file(genopts.yml.in genopts.yml @ONLY)
+
+# Define the command that will generate config_options.hpp and keywords.hpp.
+# It will be executed since there is a direct dependency between 'config'
+# (defined below) and these output files.
+# We also make the command depend on file_options.yml and genopts.yml so that
+# it gets re-executed if they change.
+add_custom_command(
+ OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/config_options.hpp
+ ${CMAKE_CURRENT_BINARY_DIR}/keywords.hpp
+ COMMENT "Generating config_options.hpp, keywords.hpp"
+ DEPENDS file_options.yml ${CMAKE_CURRENT_BINARY_DIR}/genopts.yml
+ COMMAND
+ Genopts::Python3_Interpreter -m genopts --config
+ ${CMAKE_CURRENT_BINARY_DIR}/genopts.yml
+ ${CMAKE_CURRENT_LIST_DIR}/file_options.yml
+)
diff --git a/src/config/config_schema.hpp b/src/config/config_schema.hpp
deleted file mode 100644
index b14416727d249dc803dc89de4f476c8c48e6b1b0..0000000000000000000000000000000000000000
--- a/src/config/config_schema.hpp
+++ /dev/null
@@ -1,90 +0,0 @@
-/******************************************************************************
- * Copyright 2021, Barcelona Supercomputing Center (BSC), Spain
- *
- * This software was partially supported by the EuroHPC-funded project ADMIRE
- * (Project ID: 956748, https://www.admire-eurohpc.eu).
- *
- * This file is part of scord.
- *
- * scord is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * scord is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with scord. If not, see .
- *
- * SPDX-License-Identifier: GPL-3.0-or-later
- *****************************************************************************/
-
-#ifndef SCORD_CONFIG_SCHEMA_HPP
-#define SCORD_CONFIG_SCHEMA_HPP
-
-#include
-
-#include "file_options/file_options.hpp"
-#include "parsers.hpp"
-#include "keywords.hpp"
-#include "defaults.hpp"
-
-namespace fs = std::filesystem;
-
-namespace scord::config {
-
-using file_options::converter;
-using file_options::declare_file;
-using file_options::declare_group;
-using file_options::declare_list;
-using file_options::declare_option;
-using file_options::file_schema;
-using file_options::opt_type;
-using file_options::sec_type;
-
-// define the configuration file structure and declare the supported options
-const file_schema valid_options = declare_file({
- // section for global settings
- declare_section(
- keywords::global_settings, sec_type::mandatory,
- declare_group({
-
- declare_option(
- keywords::use_syslog, opt_type::mandatory,
- converter(parsers::parse_bool)),
-
- declare_option(
- keywords::log_file, opt_type::optional,
- converter(parsers::parse_path)),
-
- declare_option(
- keywords::log_file_max_size, opt_type::optional,
- converter(parsers::parse_capacity)),
-
- declare_option(
- keywords::transport_protocol,
- opt_type::mandatory),
-
- declare_option(keywords::bind_address,
- opt_type::mandatory),
-
- declare_option(
- keywords::remote_port, opt_type::mandatory,
- converter(parsers::parse_number)),
-
- declare_option(
- keywords::pidfile, opt_type::mandatory,
- converter(parsers::parse_path)),
-
- declare_option(
- keywords::workers, opt_type::mandatory,
- converter(parsers::parse_number)),
- })),
-});
-
-} // namespace scord::config
-
-#endif /* SCORD_CONFIG_SCHEMA_HPP */
diff --git a/src/config/file_options.yml b/src/config/file_options.yml
new file mode 100644
index 0000000000000000000000000000000000000000..7049676e7999b51b8d69797cfcfca7154fbaca3a
--- /dev/null
+++ b/src/config/file_options.yml
@@ -0,0 +1,41 @@
+sections:
+ - name: "global_settings"
+ required: true
+ options:
+ - name: "use_syslog"
+ required: true
+ type: "bool"
+ converter: "parsers::parse_bool"
+
+ - name: "log_file"
+ required: false
+ type: "std::filesystem::path"
+ converter: "parsers::parse_path"
+
+ - name: "log_file_max_size"
+ required: false
+ type: "uint32_t"
+ converter: "parsers::parse_capacity"
+
+ - name: "transport_protocol"
+ required: true
+ type: "std::string"
+
+ - name: "bind_address"
+ required: true
+ type: "std::string"
+
+ - name: "remote_port"
+ required: true
+ type: "uint32_t"
+ converter: "parsers::parse_number"
+
+ - name: "pidfile"
+ required: true
+ type: "std::filesystem::path"
+ converter: "parsers::parse_path"
+
+ - name: "workers"
+ required: true
+ type: "uint32_t"
+ converter: "parsers::parse_number"
diff --git a/src/config/file_options/options_description.hpp b/src/config/file_options/options_description.hpp
deleted file mode 100644
index 643a14266226003dcfa2adf1f592a4618babb19d..0000000000000000000000000000000000000000
--- a/src/config/file_options/options_description.hpp
+++ /dev/null
@@ -1,123 +0,0 @@
-/******************************************************************************
- * Copyright 2021, Barcelona Supercomputing Center (BSC), Spain
- *
- * This software was partially supported by the EuroHPC-funded project ADMIRE
- * (Project ID: 956748, https://www.admire-eurohpc.eu).
- *
- * This file is part of scord.
- *
- * scord is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * scord is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with scord. If not, see .
- *
- * SPDX-License-Identifier: GPL-3.0-or-later
- *****************************************************************************/
-
-/*
- * This file contains the code to implement an options map that can
- * be generated by parsing a configuration according to the structure
- * defined by a file_schema instance.
- */
-
-#ifndef SCORD_CONFIG_FILE_OPTIONS_DESCRIPTION_HPP
-#define SCORD_CONFIG_FILE_OPTIONS_DESCRIPTION_HPP
-
-#include
-#include
-#include
-
-namespace file_options {
-
-/*! Class to handle values of an arbitrary type */
-struct option_value {
- /*! Constructor. The 'value' parameter is passed (and stored)
- * as boost::any in order to handle arbitrarily-typed values */
- explicit option_value(boost::any value) : m_value(std::move(value)) {}
-
- /*! If the stored value is of type T, returns that value.
- * Otherwise throws boost::bad_any_cast exception */
- template
- T&
- get_as() {
- return boost::any_cast(m_value);
- }
-
- /*! If the stored value is of type T, returns that value.
- * Otherwise throws boost::bad_any_cast exception */
- template
- const T&
- get_as() const {
- return boost::any_cast(m_value);
- }
-
- /*! Stored option value */
- boost::any m_value;
-};
-
-/*! Class to handle a group of related options. The stored options
- * can be accessed using the usual std::map interface. */
-struct options_group : public std::map {
-
- bool
- has(const std::string& opt_name) const {
- return count(opt_name) != 0;
- }
-
- /*! If the options_group contains an option named 'opt_name'
- * of type T, returns the value for that option.
- * If the option does not exist, throws std::invalid_argument exception.
- * Otherwise throws boost::bad_any_cast exception */
- template
- const T&
- get_as(const std::string& opt_name) const {
-
- auto it = find(opt_name);
-
- if(it == end()) {
- throw std::invalid_argument("option '" + opt_name + "' missing");
- }
-
- return it->second.get_as();
- }
-};
-
-/*! Class to handle a list of several groups */
-using options_list = std::vector;
-
-/*! Class that stores the values for the defined variables so that they
- * can be accessed in a key-value manner.
- * This class is derived from std::map,
- * therefore all usual std::map operators can be used to access
- * its contents */
-struct options_map : public std::map {
-
- /*! If the options_map contains an option named 'opt_name'
- * of type T, returns the value for that option.
- * If the option does not exist, throws std::invalid_argument exception.
- * Otherwise throws boost::bad_any_cast exception */
- template
- T&
- get_as(const std::string& opt_name) {
-
- auto it = find(opt_name);
-
- if(it == end()) {
- throw std::invalid_argument("option '" + opt_name + "' missing");
- }
-
- return boost::any_cast(it->second);
- }
-};
-
-} // namespace file_options
-
-#endif /* SCORD_CONFIG_FILE_OPTIONS_DESCRIPTION_HPP */
diff --git a/src/config/file_options/schema.hpp b/src/config/file_options/schema.hpp
deleted file mode 100644
index ea825d30408c908bec9e348020432e7cfe16bda8..0000000000000000000000000000000000000000
--- a/src/config/file_options/schema.hpp
+++ /dev/null
@@ -1,414 +0,0 @@
-/******************************************************************************
- * Copyright 2021, Barcelona Supercomputing Center (BSC), Spain
- *
- * This software was partially supported by the EuroHPC-funded project ADMIRE
- * (Project ID: 956748, https://www.admire-eurohpc.eu).
- *
- * This file is part of scord.
- *
- * scord is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * scord is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with scord. If not, see .
- *
- * SPDX-License-Identifier: GPL-3.0-or-later
- *****************************************************************************/
-
-/*
- * This file implements the classes necessary to describe the structure
- * of a configuration file, as well as the options that are accepted and
- * how to process them when found.
- *
- * A typical configuration file can be defined as follows:
- *
- * const file_schema config_schema = declare_file({
- * declare_section(
- * "section0", // section name
- * true, // section is mandatory
- * declare_group({ // define a group of related options
- * declare_option<
- * uint32_t // type for option value to be converted to
- * >(
- * "option0", // option name in file
- * true, // mandatory
- * 8, // default value (optional)
- * number_parser // converter function
- * // std::string -> uint32_t (optional)
- * ),
- * {{ other option definitions... }}
- * })
- * ),
- * declare_section(
- * "section1", // section name
- * true, // section is mandatory
- * declare_list({ // define a list of options that can appear
- * // several times
- * declare_option<
- * std::string // type for option value to be converted to
- * >(
- * "option1", // option name in file
- * true // mandatory
- * ),
- * declare_option<
- * std::string // type for option value to be converted to
- * >(
- * "option2", // option name in file
- * false // mandatory
- * )
- * })
- * ),
- * {{ other section definitions... }}
- * });
- *
- */
-
-#ifndef SCORD_CONFIG_FILE_OPTIONS_SCHEMA_HPP
-#define SCORD_CONFIG_FILE_OPTIONS_SCHEMA_HPP
-
-#include
-#include
-#include
-
-//#include "utils.h"
-
-namespace file_options {
-
-/*! Special option name that can be used to accept any non-defined
- * options in list_schema definitions */
-static const std::string match_any = ".*";
-
-// forward declarations
-struct section_schema;
-struct option_schema;
-
-enum class opt_type : bool {
- optional,
- mandatory,
-};
-
-enum class sec_type : bool {
- optional,
- mandatory,
-};
-
-enum class grp_type : bool {
- optional,
- mandatory,
-};
-
-
-/*! Class to represent a file organization as a key-value map
- * of section definitions, where file_schema[section_name] returns
- * the parsed section contents.
- *
- * Since we are implicitly using std::map, all its typical
- * functions can be used */
-using file_schema = std::map;
-
-
-/*! Class to handle a group of related options as a key-value map
- * where group_schema[option_name] returns the parsed (and converted)
- * option_value
- * Since we are implicitly using std::map, all its typical
- * functions can be used */
-using group_schema = std::map;
-
-
-/*! Class to represent a list of options that can be arbitrarily
- * repeated in a section.
- *
- * Since we inherit from group_schema, which is implicitly using
- * std::map, all its typical functions can be used */
-struct list_schema : public group_schema {
- // inherit constructors from group_schema
- using group_schema::group_schema;
-};
-
-/*! Abstract base class to handle the semantics of a configuration option.
- * By deriving specialized classes from this base class, we can provide
- * a simple interface for the user to define an option (see typed_value). */
-struct option_type {
- /*! Returns true if a default value was provided for this option when
- * constructed */
- virtual bool
- has_default_value() const = 0;
-
- /*! Returns true if a converter function was provided for this option when
- * constructed */
- virtual bool
- has_converter() const = 0;
-
- /*! Returns the default value of this function if it was provided
- * when constructed. If it was not provided, the return value is
- * undefined. */
- virtual boost::any
- default_value() const = 0;
-
- /*! Conversion function from std::string to the type defined for the option
- */
- virtual boost::any
- convert(const std::string& key, const std::string& value) const = 0;
-
- /*! Destructor to avoid leaking */
- virtual ~option_type() {}
-};
-
-/*! Type definition for conversion function */
-template
-using ParserFun = std::function;
-
-/*! Public helper trait for conversion function type */
-template
-using converter = ParserFun;
-
-/*! Concrete class to handle the semantics of a configuration option
- * of type T. It stores the default value of type T provided by the user,
- * as well as a specialized conversion function std::string -> T to be used,
- * when parsing the option's value. */
-template
-struct typed_value : public option_type {
-
- /*! Constructor for an option without default value
- * or conversion function */
- typed_value() : m_has_default_value(false), m_has_converter(false) {}
-
- /*! Constructor for an option with default value
- * and no conversion function */
- explicit typed_value(const T default_value)
- : m_has_default_value(true), m_default_value(default_value),
- m_has_converter(false) {}
-
- /*! Constructor for an option with a conversion
- * function and no default value */
- explicit typed_value(const ParserFun& converter)
- : m_has_default_value(false), m_has_converter(true),
- m_converter(converter) {}
-
- /*! Constructor for an option with a default value
- * and a conversion function */
- typed_value(const T default_value, const ParserFun& converter)
- : m_has_default_value(true), m_default_value(default_value),
- m_has_converter(true), m_converter(converter) {}
-
- /*! Returns true if a default value was provided for this option when
- * constructed */
- bool
- has_default_value() const override {
- return m_has_default_value;
- }
-
- /*! Returns true if a converter function was provided for this option when
- * constructed */
- bool
- has_converter() const override {
- return m_has_converter;
- }
-
- /*! Returns the default value of this function if it was provided
- * when constructed. If it was not provided, the return value is
- * undefined. */
- boost::any
- default_value() const override {
- return m_default_value;
- }
-
- /*! If a converter function has been provided for this option, the
- * function is applied to 'value' and the result (hopefully of type T) is
- * returned to the caller. If no function is provided, an undefined value
- * is returned, given that we don't know how to transform std::string to T.
- * If the conversion function fails, the exception is propagated to the user
- */
- boost::any
- convert(const std::string& key, const std::string& value) const override {
-
- if(m_has_converter) {
- return m_converter(key, value);
- }
-
- return {};
- }
-
- bool m_has_default_value; /*!< True if a default value was provided */
- T m_default_value; /*!< Default value provided by the user */
- bool m_has_converter; /*!< True if a conversion function was provided */
- ParserFun m_converter; /*!< Conversion fucntion provided by the user */
-};
-
-/*! Creates a typed_value instance. This function is the primary method
- * to create a typed_value instance for a specific type, which can later be
- * passed to the group_schema and list_schema constructors.
- *
- * Overload without default value or conversion function */
-template
-std::shared_ptr>
-type() {
- return std::make_shared>();
-}
-
-/*! Creates a typed_value instance. This function is the primary method
- * to create a typed_value instance for a specific type, which can later be
- * passed to the group_schema and list_schema constructors.
- *
- * Overload with default value only */
-template
-std::shared_ptr>
-type(const T& default_value) {
- return std::make_shared>(default_value);
-}
-
-/*! Creates a typed_value instance. This function is the primary method
- * to create a typed_value instance for a specific type, which can later be
- * passed to the group_schema and list_schema constructors.
- *
- * Overload with conversion function only */
-template
-std::shared_ptr>
-type(const ParserFun& converter) {
- return std::make_shared>(converter);
-}
-
-/*! Creates a typed_value instance. This function is the primary method
- * to create a typed_value instance for a specific type, which can later be
- * passed to the group_schema and list_schema constructors.
- *
- * Overload with default value and conversion function */
-template
-std::shared_ptr>
-type(const T& default_value, const ParserFun& converter) {
- return std::make_shared>(default_value, converter);
-}
-
-/*! Class used to handle the description of a section */
-struct section_schema {
-
- using SchemaType = boost::variant;
-
- section_schema(bool mandatory, SchemaType&& schema)
- : m_mandatory(mandatory), m_schema(schema) {}
-
- bool
- is_mandatory() const {
- return m_mandatory;
- }
-
- bool m_mandatory;
- SchemaType m_schema;
-};
-
-/*! Class used to handle the description of an option */
-struct option_schema {
-
- /*! Creates an instance of option_schema and stores whether
- * the option is mandatory or not, and also its type */
- option_schema(bool mandatory, std::shared_ptr type)
- : m_mandatory(mandatory), m_typed_value(type) {}
-
- /*! Returns true if the option has been defined as mandatory */
- bool
- is_mandatory() const {
- return m_mandatory;
- }
-
- /*! Returns true if a default value has been defined for the option */
- bool
- has_default_value() const {
- return m_typed_value->has_default_value();
- }
-
- /*! Returns the default value defined for m_typed_value, if any.
- * Otherwise, the return value is undefined */
- boost::any
- default_value() const {
- return m_typed_value->default_value();
- }
-
- /*! Returns true if a converter function has been defined for m_typed_value
- */
- bool
- has_converter() const {
- return m_typed_value->has_converter();
- }
-
- /*! Calls the converter function defined for m_typed_value, if any.
- * Otherwise, the result is undefined */
- boost::any
- convert(const std::string& name, const std::string& value) const {
- return m_typed_value->convert(name, value);
- }
-
- bool m_mandatory; /*!< True if the option is mandatory */
- std::shared_ptr
- m_typed_value; /*!< Specific semantics for this option */
-};
-
-// some helpers to simplify the syntax of a file declaration
-
-using OptionSchemaType = std::pair;
-
-template
-OptionSchemaType
-declare_option(const std::string& name, opt_type ot) {
- return {name, {static_cast(ot), type()}};
-}
-
-template
-OptionSchemaType
-declare_option(const std::string& name, opt_type ot, const T& default_value) {
- return {name, {static_cast(ot), type(default_value)}};
-}
-
-template
-OptionSchemaType
-declare_option(const std::string& name, opt_type ot,
- const ParserFun& parser) {
- return {name, {static_cast(ot), type(parser)}};
-}
-
-template
-OptionSchemaType
-declare_option(const std::string& name, opt_type ot, const T& default_value,
- const ParserFun& parser) {
- return {name, {static_cast(ot), type(default_value, parser)}};
-}
-
-list_schema
-declare_list(const std::initializer_list&& args) {
- return {args};
-}
-
-group_schema
-declare_group(const std::initializer_list&& args) {
- return {args};
-}
-
-using SectionSchemaType = std::pair;
-
-SectionSchemaType
-declare_section(const std::string& name, sec_type st,
- const group_schema&& schema) {
- return {name, {static_cast(st), {schema}}};
-}
-
-SectionSchemaType
-declare_section(const std::string& name, sec_type st,
- const list_schema&& schema) {
- return {name, {static_cast(st), {schema}}};
-}
-
-/*! Declares a file_schema as a list of section_schema definitions */
-file_schema
-declare_file(const std::initializer_list&& args) {
- return {args};
-}
-
-} // namespace file_options
-
-#endif /* SCORD_CONFIG_FILE_OPTIONS_SCHEMA_HPP */
diff --git a/src/config/file_options/yaml_parser.hpp b/src/config/file_options/yaml_parser.hpp
deleted file mode 100644
index 96a462e9ea670c0519d2f46fbf43e11bf1266698..0000000000000000000000000000000000000000
--- a/src/config/file_options/yaml_parser.hpp
+++ /dev/null
@@ -1,244 +0,0 @@
-/******************************************************************************
- * Copyright 2021, Barcelona Supercomputing Center (BSC), Spain
- *
- * This software was partially supported by the EuroHPC-funded project ADMIRE
- * (Project ID: 956748, https://www.admire-eurohpc.eu).
- *
- * This file is part of scord.
- *
- * scord is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * scord is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with scord. If not, see .
- *
- * SPDX-License-Identifier: GPL-3.0-or-later
- *****************************************************************************/
-
-#ifndef SCORD_CONFIG_FILE_OPTIONS_YAML_PARSER_HPP
-#define SCORD_CONFIG_FILE_OPTIONS_YAML_PARSER_HPP
-
-/*
- * This file contains the code to implement a YAML configuration file
- * parser, that reads the file structure from a file_schema instance
- * and returns an options_map with the parsed values as a result
- */
-
-#include
-#include
-
-#include "schema.hpp"
-#include "options_description.hpp"
-
-namespace fs = std::filesystem;
-
-namespace file_options {
-namespace detail {
-
-/*! This class implements a visitor that processes a 'file_schema' instance
- * and parses a corresponding YAML-formatted file according to the options
- * described in that schema. */
-class yaml_visitor : public boost::static_visitor<> {
-public:
- /*! Constructor. */
- yaml_visitor(std::string name, const YAML::Node& config,
- options_map& opt_map)
- : m_name(std::move(name)), m_config(config), m_options_map(opt_map) {}
-
- /*! Process a 'group_schema'.
- * A 'group_schema' is a named section in a 'file_schema' that
- * describes a related group of options:
- *
- * "named-group": [
- * opt0: "foo",
- * opt1: "42"
- * ]
- *
- * As such, a 'group_schema' represents a YAML sequence of entries,
- * where each entry corresponds to a map of key-value pairs
- */
- void
- operator()(group_schema& opt_group) const {
-
- std::set mandatory_options;
- std::map default_values;
-
- // store any mandatory options so that we can check later if
- // the user provided them
- for(const auto& opt : opt_group) {
-
- const auto& opt_name = opt.first;
- const auto& opt_schema = opt.second;
-
- if(opt_schema.is_mandatory()) {
- mandatory_options.emplace(opt_name);
- }
-
- if(opt_schema.has_default_value()) {
- default_values.emplace(opt_name, opt_schema.default_value());
- }
- }
-
- options_group parsed_values;
-
- for(const auto& entry : m_config) {
- for(const auto& kv : entry) {
- const auto key = kv.first.as();
- const auto text_value = kv.second.as();
-
- auto it = opt_group.find(key);
-
- if(it == opt_group.end()) {
- throw std::invalid_argument("Invalid argument '" + key +
- "' in '" + m_name + "'");
- }
-
- auto& opt_schema = it->second;
-
- boost::any value;
-
- if(opt_schema.has_converter()) {
- value = opt_schema.convert(key, text_value);
- } else {
- value = text_value;
- }
-
- parsed_values.emplace(key, option_value(value));
- }
- }
-
- // check whether all mandatory options were provided
- for(const auto& opt_name : mandatory_options) {
- if(parsed_values.count(opt_name) == 0) {
- throw std::invalid_argument("Missing mandatory option '" +
- opt_name + "'");
- }
- }
-
- // non-mandatory options not-provided by the user need to be
- // initialized to their default values (if any)
- for(const auto& v : default_values) {
-
- const auto& opt_name = v.first;
- const auto& opt_value = v.second;
-
- if(parsed_values.count(opt_name) == 0) {
- parsed_values.emplace(opt_name, opt_value);
- }
- }
-
- m_options_map.emplace(m_name, std::move(parsed_values));
- }
-
- /*! Process a 'list_schema'.
- * A 'list_schema' is a named section in a 'file_schema' that
- * describes a list of repeated group of options, as follows:
- *
- * "named-list": [
- * [ opt0: "foo",
- * opt1: "42",
- * opt3: "baz" ],
- *
- * [ opt0: "bar",
- * opt1: "6" ]
- * ]
- *
- * As such, a 'list_schema' represents a YAML sequence of entries,
- * where each entry corresponds to another sequence where
- * each of its entries is a map of key-value pairs
- */
- void
- operator()(list_schema& opt_list) const {
- (void) opt_list;
-
- bool accept_all = (opt_list.count(match_any) != 0);
-
- options_list parsed_values;
-
- for(const auto& entry : m_config) {
-
- options_group parsed_group;
-
- for(const auto& subentry : entry) {
- for(const auto& kv : subentry) {
- const auto key = kv.first.as();
- const auto text_value = kv.second.as();
-
- auto it = opt_list.find(key);
-
- if(it == opt_list.end()) {
- if(!accept_all) {
- throw std::invalid_argument("Invalid argument '" +
- key + "' in '" +
- m_name + "'");
- }
-
- it = opt_list.find(match_any);
-
- assert(it != opt_list.end());
- }
-
- auto& opt_schema = it->second;
-
- boost::any value;
-
- if(opt_schema.has_converter()) {
- value = opt_schema.convert(key, text_value);
- } else {
- value = text_value;
- }
-
- parsed_group.emplace(key, option_value(value));
- }
- }
-
- parsed_values.emplace_back(parsed_group);
- }
-
- m_options_map.emplace(m_name, std::move(parsed_values));
- }
-
- const std::string m_name;
- const YAML::Node& m_config;
- options_map& m_options_map;
-};
-
-} // namespace detail
-
-/*! Parses 'yaml_file' and stores in 'opt_map' the values of any options defined
- * in 'schema' */
-void
-parse_yaml_file(const fs::path& yaml_file, const file_schema& schema,
- options_map& opt_map) {
-
- const YAML::Node config = YAML::LoadFile(yaml_file.string());
-
- for(const auto& opt : schema) {
-
- auto opt_name = opt.first;
- auto opt_schema = opt.second;
-
- if(!config[opt_name]) {
- if(opt_schema.is_mandatory()) {
- throw std::invalid_argument("Mandatory section '" + opt_name +
- "' is missing");
- }
- continue;
- }
-
- boost::apply_visitor(
- detail::yaml_visitor(opt_name, config[opt_name], opt_map),
- opt_schema.m_schema);
- }
-}
-
-} // namespace file_options
-
-#endif /* SCORD_CONFIG_FILE_OPTIONS_YAML_PARSER_HPP */
diff --git a/src/config/genopts.yml.in b/src/config/genopts.yml.in
new file mode 100644
index 0000000000000000000000000000000000000000..fae2d6293b6668b518b5d0d86e6560de37ce9978
--- /dev/null
+++ b/src/config/genopts.yml.in
@@ -0,0 +1,14 @@
+genopts:
+ schema:
+ copyright_file: "@CMAKE_SOURCE_DIR@/COPYRIGHT_NOTICE"
+ header_guard: "SCORD_CONFIG_SCHEMA_HPP"
+ namespace: "scord::config"
+ output_path:
+ header: "@CMAKE_CURRENT_BINARY_DIR@/config_options.hpp"
+
+ keywords:
+ copyright_file: "@CMAKE_SOURCE_DIR@/COPYRIGHT_NOTICE"
+ header_guard: "SCORD_CONFIG_KEYWORDS_HPP"
+ namespace: "scord::config::keywords"
+ output_path:
+ header: "@CMAKE_CURRENT_BINARY_DIR@/keywords.hpp"
diff --git a/src/config/keywords.hpp b/src/config/keywords.hpp
deleted file mode 100644
index 6864cfe7f48c0f2837ddf8e1422d1c65790d3743..0000000000000000000000000000000000000000
--- a/src/config/keywords.hpp
+++ /dev/null
@@ -1,55 +0,0 @@
-/******************************************************************************
- * Copyright 2021, Barcelona Supercomputing Center (BSC), Spain
- *
- * This software was partially supported by the EuroHPC-funded project ADMIRE
- * (Project ID: 956748, https://www.admire-eurohpc.eu).
- *
- * This file is part of scord.
- *
- * scord is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * scord is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with scord. If not, see .
- *
- * SPDX-License-Identifier: GPL-3.0-or-later
- *****************************************************************************/
-
-#ifndef SCORD_CONFIG_KEYWORDS_HPP
-#define SCORD_CONFIG_KEYWORDS_HPP
-
-namespace scord::config::keywords {
-
-// section names
-constexpr static const auto global_settings = "global_settings";
-constexpr static const auto namespaces = "namespaces";
-
-// option names for 'global-settings' section
-constexpr static const auto use_syslog = "use_syslog";
-constexpr static const auto log_file = "log_file";
-constexpr static const auto log_file_max_size = "log_file_max_size";
-constexpr static const auto transport_protocol = "transport_protocol";
-constexpr static const auto bind_address = "bind_address";
-constexpr static const auto remote_port = "remote_port";
-constexpr static const auto pidfile = "pidfile";
-constexpr static const auto workers = "workers";
-constexpr static const auto staging_directory = "staging_directory";
-
-// option names for 'namespaces' section
-constexpr static const auto nsid = "nsid";
-constexpr static const auto track_contents = "track_contents";
-constexpr static const auto mountpoint = "mountpoint";
-constexpr static const auto type = "type";
-constexpr static const auto capacity = "capacity";
-constexpr static const auto visibility = "visibility";
-
-} // namespace scord::config::keywords
-
-#endif /* SCORD_CONFIG_KEYWORDS_HPP */
diff --git a/src/config/settings.cpp b/src/config/settings.cpp
index 1e9af35a4b23793f24702360bb4a300e04e90194..0987446dff19738bf82c683922a6103587ce8e23 100644
--- a/src/config/settings.cpp
+++ b/src/config/settings.cpp
@@ -24,7 +24,7 @@
#include
#include
-#include "config_schema.hpp"
+#include "config_options.hpp"
#include "defaults.hpp"
#include "file_options/options_description.hpp"
#include "file_options/yaml_parser.hpp"