|
| 1 | +#[==[ |
| 2 | +@file f3dOptions.cmake |
| 3 | +
|
| 4 | +This module contains all the methods required to parse a JSON file specifying options |
| 5 | +and generate the associated CXX code. |
| 6 | +#]==] |
| 7 | + |
| 8 | +#[==[ |
| 9 | +@brief generate public and private headers for a provided options.json |
| 10 | +
|
| 11 | +~~~ |
| 12 | +f3d_generate_options( |
| 13 | + INPUT_JSON "path/to/options.json" |
| 14 | + INPUT_PUBLIC_HEADER "path/to/options.h.in" |
| 15 | + INPUT_PRIVATE_HEADER "path/to/options_tools.h.in" |
| 16 | + DESTINATION "path/to/destination/folder" |
| 17 | + OUTPUT_NAME "options" |
| 18 | + ) |
| 19 | +~~~ |
| 20 | +
|
| 21 | +#]==] |
| 22 | + |
| 23 | +function (f3d_generate_options) |
| 24 | + cmake_parse_arguments(PARSE_ARGV 0 _f3d_generate_options |
| 25 | + "" |
| 26 | + "INPUT_JSON;INPUT_PUBLIC_HEADER;INPUT_PRIVATE_HEADER;DESTINATION;OUTPUT_NAME" |
| 27 | + "") |
| 28 | + |
| 29 | + if (_f3d_generate_options_UNPARSED_ARGUMENTS) |
| 30 | + message(FATAL_ERROR |
| 31 | + "Unparsed arguments for f3d_generate_options: " |
| 32 | + "${_f3d_generate_options_UNPARSED_ARGUMENTS}") |
| 33 | + endif () |
| 34 | + |
| 35 | + if (NOT DEFINED _f3d_generate_options_INPUT_JSON) |
| 36 | + message(FATAL_ERROR |
| 37 | + "Missing INPUT_JSON argument for f3d_generate_options") |
| 38 | + endif () |
| 39 | + |
| 40 | + if (NOT DEFINED _f3d_generate_options_INPUT_PUBLIC_HEADER) |
| 41 | + message(FATAL_ERROR |
| 42 | + "Missing INPUT_PUBLIC_HEADER argument for f3d_generate_options") |
| 43 | + endif () |
| 44 | + |
| 45 | + if (NOT DEFINED _f3d_generate_options_INPUT_PRIVATE_HEADER) |
| 46 | + message(FATAL_ERROR |
| 47 | + "Missing INPUT_PRIVATE_HEADER argument for f3d_generate_options") |
| 48 | + endif () |
| 49 | + |
| 50 | + if (NOT DEFINED _f3d_generate_options_DESTINATION) |
| 51 | + message(FATAL_ERROR |
| 52 | + "Missing DESTINATION argument for f3d_generate_options") |
| 53 | + endif () |
| 54 | + |
| 55 | + if (NOT DEFINED _f3d_generate_options_OUTPUT_NAME) |
| 56 | + message(FATAL_ERROR |
| 57 | + "Missing OUTPUT_NAME argument for f3d_generate_options") |
| 58 | + endif () |
| 59 | + |
| 60 | + |
| 61 | + # Parse options.json and generate headers |
| 62 | + set(_option_basename "") |
| 63 | + set(_option_indent "") |
| 64 | + |
| 65 | + # Add a configure depends on the input file |
| 66 | + set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${_f3d_generate_options_INPUT_JSON}) |
| 67 | + |
| 68 | + ## Read the .json file and complete the struct |
| 69 | + file(READ ${_f3d_generate_options_INPUT_JSON} _options_json) |
| 70 | + _parse_json_option(${_options_json}) |
| 71 | + |
| 72 | + list(JOIN _options_setter ";\n else " _options_setter) |
| 73 | + list(JOIN _options_getter ";\n else " _options_getter) |
| 74 | + list(JOIN _options_string_setter ";\n else " _options_string_setter) |
| 75 | + list(JOIN _options_string_getter ";\n else " _options_string_getter) |
| 76 | + list(JOIN _options_lister ",\n " _options_lister) |
| 77 | + |
| 78 | + configure_file( |
| 79 | + "${_f3d_generate_options_INPUT_PUBLIC_HEADER}" |
| 80 | + "${_f3d_generate_options_DESTINATION}/public/${_f3d_generate_options_OUTPUT_NAME}.h") |
| 81 | + |
| 82 | + configure_file( |
| 83 | + "${_f3d_generate_options_INPUT_PRIVATE_HEADER}" |
| 84 | + "${_f3d_generate_options_DESTINATION}/private/${_f3d_generate_options_OUTPUT_NAME}_tools.h") |
| 85 | + |
| 86 | +endfunction() |
| 87 | + |
| 88 | +#[==[ |
| 89 | +@brief internal recursive method use to parse json structure and generate variables |
| 90 | +#]==] |
| 91 | +function(_parse_json_option _top_json) |
| 92 | + # TODO Add a deprecation mechanism |
| 93 | + string(JSON _options_length LENGTH ${_top_json}) |
| 94 | + MATH(EXPR _options_length "${_options_length} - 1") |
| 95 | + foreach(_json_idx RANGE ${_options_length}) |
| 96 | + # Recover the json element name |
| 97 | + string(JSON _member_name MEMBER ${_top_json} ${_json_idx}) |
| 98 | + # Read the json element |
| 99 | + string(JSON _cur_json GET ${_top_json} ${_member_name}) |
| 100 | + # Recover its type and default if it is a leaf option |
| 101 | + string(JSON _option_type ERROR_VARIABLE _type_error GET ${_cur_json} "type") |
| 102 | + string(JSON _option_default_value ERROR_VARIABLE _default_value_error GET ${_cur_json} "default_value") |
| 103 | + if(_type_error STREQUAL "NOTFOUND" AND _default_value_error STREQUAL "NOTFOUND") |
| 104 | + # Leaf option found! |
| 105 | + set(_option_name "${_option_basename}${_member_name}") |
| 106 | + |
| 107 | + # Identify types |
| 108 | + set(_option_actual_type ${_option_type}) |
| 109 | + set(_option_variant_type ${_option_type}) |
| 110 | + set(_option_default_value_start "") |
| 111 | + set(_option_default_value_end "") |
| 112 | + if(_option_type STREQUAL "double_vector") |
| 113 | + set(_option_actual_type "std::vector<double>") |
| 114 | + set(_option_variant_type "std::vector<double>") |
| 115 | + set(_option_default_value_start "{") |
| 116 | + set(_option_default_value_end "}") |
| 117 | + elseif(_option_type STREQUAL "string") |
| 118 | + set(_option_actual_type "std::string") |
| 119 | + set(_option_variant_type "std::string") |
| 120 | + set(_option_default_value_start "\"") |
| 121 | + set(_option_default_value_end "\"") |
| 122 | + elseif(_option_type STREQUAL "ratio") |
| 123 | + set(_option_actual_type "f3d::ratio_t") |
| 124 | + set(_option_variant_type "double") |
| 125 | + endif() |
| 126 | + |
| 127 | + # Add option to struct and methods |
| 128 | + string(APPEND _options_struct "${_option_indent} ${_option_actual_type} ${_member_name} = ${_option_default_value_start}${_option_default_value}${_option_default_value_end};\n") |
| 129 | + list(APPEND _options_setter "if (name == \"${_option_name}\") opt.${_option_name} = std::get<${_option_variant_type}>(value)") |
| 130 | + list(APPEND _options_getter "if (name == \"${_option_name}\") return opt.${_option_name}") |
| 131 | + list(APPEND _options_string_setter "if (name == \"${_option_name}\") opt.${_option_name} = options_tools::parse<${_option_actual_type}>(str)") |
| 132 | + list(APPEND _options_string_getter "if (name == \"${_option_name}\") return options_tools::format(opt.${_option_name})") |
| 133 | + list(APPEND _options_lister "\"${_option_name}\"") |
| 134 | + |
| 135 | + else() |
| 136 | + # Group found, add in the struct and recurse |
| 137 | + set(_option_prevname ${_option_basename}) |
| 138 | + set(_option_previndent ${_option_indent}) |
| 139 | + string(APPEND _option_indent " ") |
| 140 | + string(APPEND _option_basename "${_member_name}.") |
| 141 | + string(APPEND _options_struct "${_option_indent}struct ${_member_name} {\n") |
| 142 | + _parse_json_option(${_cur_json}) |
| 143 | + string(APPEND _options_struct "${_option_indent}} ${_member_name};\n\n") |
| 144 | + set(_option_indent ${_option_previndent}) |
| 145 | + set(_option_basename ${_option_prevname}) |
| 146 | + endif() |
| 147 | + endforeach() |
| 148 | + # Set appended variables and list in the parent before leaving the recursion |
| 149 | + # Always use quotes for string variable as it contains semi-colons |
| 150 | + set(_options_struct "${_options_struct}" PARENT_SCOPE) |
| 151 | + set(_options_setter ${_options_setter} PARENT_SCOPE) |
| 152 | + set(_options_getter ${_options_getter} PARENT_SCOPE) |
| 153 | + set(_options_string_setter ${_options_string_setter} PARENT_SCOPE) |
| 154 | + set(_options_string_getter ${_options_string_getter} PARENT_SCOPE) |
| 155 | + set(_options_lister ${_options_lister} PARENT_SCOPE) |
| 156 | +endfunction() |
0 commit comments