1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
|
#[==[
@file f3dOptions.cmake
This module contains all the methods required to parse a JSON file specifying options
and generate the associated CXX code.
#]==]
#[==[
@brief generate public and private headers for a provided options.json
~~~
f3d_generate_options(
INPUT_JSON "path/to/options.json"
INPUT_PUBLIC_HEADER "path/to/options.h.in"
INPUT_PRIVATE_HEADER "path/to/options_generated.h.in"
DESTINATION "path/to/destination/folder"
OUTPUT_NAME "options"
)
~~~
#]==]
function (f3d_generate_options)
cmake_parse_arguments(PARSE_ARGV 0 _f3d_generate_options
""
"INPUT_JSON;INPUT_PUBLIC_HEADER;INPUT_PRIVATE_HEADER;DESTINATION;OUTPUT_NAME"
"")
if (_f3d_generate_options_UNPARSED_ARGUMENTS)
message(FATAL_ERROR
"Unparsed arguments for f3d_generate_options: "
"${_f3d_generate_options_UNPARSED_ARGUMENTS}")
endif ()
if (NOT DEFINED _f3d_generate_options_INPUT_JSON)
message(FATAL_ERROR
"Missing INPUT_JSON argument for f3d_generate_options")
endif ()
if (NOT DEFINED _f3d_generate_options_INPUT_PUBLIC_HEADER)
message(FATAL_ERROR
"Missing INPUT_PUBLIC_HEADER argument for f3d_generate_options")
endif ()
if (NOT DEFINED _f3d_generate_options_INPUT_PRIVATE_HEADER)
message(FATAL_ERROR
"Missing INPUT_PRIVATE_HEADER argument for f3d_generate_options")
endif ()
if (NOT DEFINED _f3d_generate_options_DESTINATION)
message(FATAL_ERROR
"Missing DESTINATION argument for f3d_generate_options")
endif ()
if (NOT DEFINED _f3d_generate_options_OUTPUT_NAME)
message(FATAL_ERROR
"Missing OUTPUT_NAME argument for f3d_generate_options")
endif ()
# Parse options.json and generate headers
set(_option_basename "")
set(_option_indent "")
# Add a configure depends on the input file
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${_f3d_generate_options_INPUT_JSON})
## Read the .json file and complete the struct
file(READ ${_f3d_generate_options_INPUT_JSON} _options_json)
_parse_json_option(${_options_json})
list(JOIN _options_setter ";\n else " _options_setter)
list(JOIN _options_getter ";\n else " _options_getter)
list(JOIN _options_string_setter ";\n else " _options_string_setter)
list(JOIN _options_string_getter ";\n else " _options_string_getter)
list(JOIN _options_lister ",\n " _options_lister)
list(JOIN _options_is_optional ";\n else " _options_is_optional)
list(JOIN _options_reset ";\n else " _options_reset)
configure_file(
"${_f3d_generate_options_INPUT_PUBLIC_HEADER}"
"${_f3d_generate_options_DESTINATION}/public/${_f3d_generate_options_OUTPUT_NAME}.h")
configure_file(
"${_f3d_generate_options_INPUT_PRIVATE_HEADER}"
"${_f3d_generate_options_DESTINATION}/private/${_f3d_generate_options_OUTPUT_NAME}_generated.h")
endfunction()
#[==[
@brief internal recursive method use to parse json structure and generate variables
#]==]
function(_parse_json_option _top_json)
string(JSON _options_length LENGTH ${_top_json})
MATH(EXPR _options_length "${_options_length} - 1")
foreach(_json_idx RANGE ${_options_length})
# Recover the json element name
string(JSON _member_name MEMBER ${_top_json} ${_json_idx})
# Read the json element
string(JSON _cur_json GET ${_top_json} ${_member_name})
# Recover its type and default if it is a leaf option
string(JSON _option_type ERROR_VARIABLE _option_type_error GET ${_cur_json} "type")
string(JSON _option_type_type ERROR_VARIABLE _option_type_type_error TYPE ${_cur_json} "type")
if(_option_type_error STREQUAL "NOTFOUND" AND ${_option_type_type} STREQUAL "STRING")
# Leaf option found!
# Recover default_value if any
string(JSON _option_default_value ERROR_VARIABLE _default_value_error GET ${_cur_json} "default_value")
# Recover deprecated if any
string(JSON _option_deprecated ERROR_VARIABLE _deprecated_error GET ${_cur_json} "deprecated")
set(_option_name "${_option_basename}${_member_name}")
# Identify types
set(_option_actual_type ${_option_type})
set(_option_variant_type ${_option_type})
set(_option_variant_convert "")
set(_option_explicit_constr "")
set(_option_default_value_start "")
set(_option_default_value_end "")
if(_option_type STREQUAL "int_vector")
set(_option_actual_type "std::vector<int>")
set(_option_variant_type "std::vector<int>")
set(_option_default_value_start "{")
set(_option_default_value_end "}")
elseif(_option_type STREQUAL "double_vector")
set(_option_actual_type "std::vector<double>")
set(_option_variant_type "std::vector<double>")
set(_option_default_value_start "{")
set(_option_default_value_end "}")
elseif(_option_type STREQUAL "string")
set(_option_actual_type "std::string")
set(_option_variant_type "std::string")
set(_option_default_value_start "\"")
set(_option_default_value_end "\"")
elseif(_option_type STREQUAL "path")
set(_option_actual_type "std::filesystem::path")
set(_option_variant_type "std::string")
set(_option_variant_convert ".string()")
set(_option_default_value_start "\"")
set(_option_default_value_end "\"")
elseif(_option_type STREQUAL "ratio")
set(_option_actual_type "f3d::ratio_t")
set(_option_explicit_constr "f3d::ratio_t")
set(_option_variant_type "double")
set(_option_default_value_start "{")
set(_option_default_value_end "}")
elseif(_option_type STREQUAL "color")
set(_option_actual_type "f3d::color_t")
set(_option_explicit_constr "f3d::color_t")
set(_option_variant_type "std::vector<double>")
set(_option_default_value_start "{")
set(_option_default_value_end "}")
elseif(_option_type STREQUAL "direction")
set(_option_actual_type "f3d::direction_t")
set(_option_explicit_constr "f3d::direction_t")
set(_option_variant_type "std::vector<double>")
set(_option_default_value_start "{")
set(_option_default_value_end "}")
elseif(_option_type STREQUAL "colormap")
set(_option_actual_type "f3d::colormap_t")
set(_option_explicit_constr "f3d::colormap_t")
set(_option_variant_type "std::vector<double>")
set(_option_default_value_start "{")
set(_option_default_value_end "}")
elseif(_option_type STREQUAL "transform2d")
set(_option_actual_type "f3d::transform2d_t")
set(_option_explicit_constr "f3d::transform2d_t")
set(_option_variant_type "std::vector<double>")
set(_option_default_value_start "{")
set(_option_default_value_end "}")
endif()
# Add option to struct and methods
set(_option_deprecated_string "")
if(_deprecated_error STREQUAL "NOTFOUND" AND _option_deprecated)
set(_option_deprecated_string "[[deprecated(\"${_option_deprecated}\")]] ")
endif()
if(_default_value_error STREQUAL "NOTFOUND")
# Use default_value
set(_optional_default_value_initialize "${_option_explicit_constr}${_option_default_value_start}${_option_default_value}${_option_default_value_end}")
string(APPEND _options_struct "${_option_indent} ${_option_deprecated_string}${_option_actual_type} ${_member_name} = ${_optional_default_value_initialize};\n")
set(_optional_getter "")
list(APPEND _options_is_optional "if (name == \"${_option_name}\") return false")
list(APPEND _options_reset "if (name == \"${_option_name}\") opt.${_option_name} = ${_optional_default_value_initialize}")
else()
# No default_value, it is an std::optional
string(APPEND _options_struct "${_option_indent} ${_option_deprecated_string}std::optional<${_option_actual_type}> ${_member_name};\n")
set(_optional_getter ".value()")
list(APPEND _options_is_optional "if (name == \"${_option_name}\") return true")
list(APPEND _options_reset "if (name == \"${_option_name}\") opt.${_option_name}.reset()")
endif()
list(APPEND _options_setter "if (name == \"${_option_name}\") opt.${_option_name} = ${_option_explicit_constr}{std::get<${_option_variant_type}>(value)}")
list(APPEND _options_getter "if (name == \"${_option_name}\") return opt.${_option_name}${_optional_getter}${_option_variant_convert}")
list(APPEND _options_string_setter "if (name == \"${_option_name}\") opt.${_option_name} = options_tools::parse<${_option_actual_type}>(str)")
list(APPEND _options_string_getter "if (name == \"${_option_name}\") return options_tools::format(opt.${_option_name}${_optional_getter})")
list(APPEND _options_lister "\"${_option_name}\"")
else()
# Group found, add in the struct and recurse
set(_option_prevname ${_option_basename})
set(_option_previndent ${_option_indent})
string(APPEND _option_indent " ")
string(APPEND _option_basename "${_member_name}.")
string(APPEND _options_struct "${_option_indent}struct ${_member_name} {\n")
_parse_json_option(${_cur_json})
string(APPEND _options_struct "${_option_indent}} ${_member_name};\n\n")
set(_option_indent ${_option_previndent})
set(_option_basename ${_option_prevname})
endif()
endforeach()
# Set appended variables and list in the parent before leaving the recursion
# Always use quotes for string variable as it contains semi-colons
set(_options_struct "${_options_struct}" PARENT_SCOPE)
set(_options_setter ${_options_setter} PARENT_SCOPE)
set(_options_getter ${_options_getter} PARENT_SCOPE)
set(_options_string_setter ${_options_string_setter} PARENT_SCOPE)
set(_options_string_getter ${_options_string_getter} PARENT_SCOPE)
set(_options_lister ${_options_lister} PARENT_SCOPE)
set(_options_is_optional ${_options_is_optional} PARENT_SCOPE)
set(_options_reset ${_options_reset} PARENT_SCOPE)
endfunction()
|