예제 #1
0
 def parameter_public_entry(self):
     return (PARAMETER_PUBLIC_ENTRY_CONST.format(
         param_class=self.param_class,
         type=self.cpp_type,
         immutable_accessor_name=to_pascal_case(self.param_name),
         param_variable_name=self.param_variable_name,
     ) if self.is_constant else PARAMETER_PUBLIC_ENTRY.format(
         param_class=self.param_class,
         type=self.cpp_type,
         immutable_accessor_name=to_pascal_case(self.param_name),
         mutable_accessor_name="Mutable" + to_pascal_case(self.param_name),
         param_variable_name=self.param_variable_name,
     ))
예제 #2
0
 def load_command_line_arg_into_config(self):
     return LOAD_COMMAND_LINE_ARG_INTO_CONFIG.format(
         param_name=self.param_name,
         param_accessor_name=to_pascal_case(self.param_name),
         dependencies="",
         arg_prefix="",
     )
예제 #3
0
 def load_command_line_arg_into_config_with_dependencies(
         self, dependencies: str, arg_prefix: str):
     return LOAD_COMMAND_LINE_ARG_INTO_CONFIG.format(
         param_name=self.param_name,
         param_accessor_name=to_pascal_case(self.param_name),
         dependencies=dependencies,
         arg_prefix=arg_prefix,
     )
예제 #4
0
    def create_header_config_list_from_metadata(
            top_level_config_name: str,
            config_metadata: dict) -> List[CppHeaderConfig]:
        """Takes the config metadata loaded by config_yaml_loader, and converts it to a list of CppHeaderConfig objects;
        this includes setting the dependency graphs needed for the configs.

        :param top_level_config_name: the name of the top level config
        :param config_metadata: the dictionary containing the config metadata
        :return: list of CppHeaderConfig objects
        """
        cpp_configs_dict = {}
        dependency_graph = nx.DiGraph()
        top_level_config = CppHeaderConfig(top_level_config_name, True)

        # first pass to construct all CppHeaderConfig objects
        for config, metadata in config_metadata.items():
            config_name = to_pascal_case(config.split(".")[0])

            config = CppHeaderConfig(config_name)
            top_level_config.include_config(config)

            if PARAMETER_KEY in metadata:
                for parameter in metadata[PARAMETER_KEY]:
                    param_metadata = list(parameter.values())[0]
                    param_type = list(parameter.keys())[0]

                    cpp_param = CppParameter(param_type, param_metadata)
                    config.add_parameter(cpp_param)

            cpp_configs_dict[config_name] = config
            dependency_graph.add_node(config_name, config=config)

        # second pass to create dependency graph
        for config, metadata in config_metadata.items():
            config_name = to_pascal_case(config.split(".")[0])

            config = cpp_configs_dict[config_name]

            if INCLUDE_KEY in metadata:
                for included_yaml in metadata[INCLUDE_KEY]:
                    included_config_name = to_pascal_case(
                        included_yaml.split(".")[0])
                    config.include_config(
                        cpp_configs_dict[included_config_name])
                    # add an edge from config node to included config node
                    dependency_graph.add_edge(config_name,
                                              included_config_name)

        # for each node, create a subgraph of relevant dependencies
        # Note: This can be optimized by doing traversal from each source, and creating subgraphs for
        # all its descendants during the same traversal
        for node in dependency_graph.nodes:
            # find the subgraph of the dependency graph relevant to the current node
            dependency_graph.nodes[node][
                "config"].dependency_graph = dependency_graph.subgraph(
                    nx.algorithms.dag.descendants(dependency_graph, node))

        top_level_config.dependency_graph = dependency_graph
        cpp_configs = [
            dependency_graph.nodes[node]["config"] for node in list(
                reversed(list(nx.topological_sort(dependency_graph))))
        ]
        cpp_configs.append(top_level_config)

        return cpp_configs
예제 #5
0
    def create_config_list_from_metadata(
        top_level_config_name, config_metadata
    ) -> List[CConfig]:
        """Takes the config metadata loaded by config_yaml_loader and converts
        it to a list of CConfig objects. The CConfig objects contain useful
        string representations that can directly be written to the generated file.

        Note: The top level config includes all other configs by default.

        :param top_level_config_name: The name of the "top level" config that contains all configs
        :param config_metadata: The output of config_yaml_loader
        :returns: list of CConfig

        """
        c_configs = []

        top_level_config = CConfig(
            top_level_config_name, f"{top_level_config_name}_ptr"
        )

        for config, metadata in config_metadata.items():

            # we convert the yaml file name to pascal case
            # and store that as the config_name
            config_name = to_pascal_case(config.split(".")[0])

            config = CConfig(
                config_name, "{}_ptr->{}".format(top_level_config_name, config_name)
            )

            # a config can be empty if it has NO constant parameters
            # an empty config will get generated as an empty struct which is
            # NOT allowed in C, this flag will make sure we don't generate empty_configs
            # which is a config that has neither parameters nor includes
            config_empty = True

            # add all the valid CParameters to the CConfig
            if PARAMETER_KEY in metadata:
                for parameter in metadata[PARAMETER_KEY]:

                    param_metadata = list(parameter.values())[0]
                    param_type = list(parameter.keys())[0]

                    if CONSTANT_KEY in param_metadata:
                        if param_type not in C_TYPE_MAP:
                            # we need to ignore parameters that are
                            # meant for cpp parameters (ex. factory, enum)
                            continue

                        param_type = C_TYPE_MAP[param_type]

                        # this is the fully resolved pointer access
                        # to the location of this parameter
                        ptr_to_instance = "{}->{}".format(
                            config.ptr_to_instance, param_metadata["name"]
                        )

                        c_param = CParameter(
                            param_metadata["name"], param_type, param_metadata["value"],
                        )

                        config.add_parameter(c_param)
                        config_empty = False

            # add all the valid included configs to the CConfig
            if INCLUDE_KEY in metadata:
                for included_yaml in metadata["include"]:
                    config.include_config(to_pascal_case(included_yaml.split(".")[0]))
                    config_empty = False

            if not config_empty:
                top_level_config.include_config(config_name)
                c_configs.append(config)

        c_configs.append(top_level_config)

        return c_configs
예제 #6
0
    def write_config_metadata_proto(
        output_proto: str,
        top_level_proto: str,
        config_metadata: dict,
    ):
        """Generates the .proto file contain all the protobuf representations
        of all dynamic parameter configs.

        :param output_proto: the name of the proto
        :param top_level_proto: the top level proto name
        :param config_metadata: the dictionary containing the config metadata

        """
        output_proto_contents = ""
        list_of_includes = []

        for config, config_definition in config_metadata.items():
            message_contents = ""
            entry_count = 1
            name = to_pascal_case(config.split(".")[0])
            list_of_includes.append(config)

            # generate includes
            if "include" in config_definition:
                for included_config in config_definition["include"]:

                    # There is no way to forward declare messages in proto
                    # so lets make use of google.protobuf.Any to store nested
                    # configs
                    #
                    # Since we are autogenerating, we should know which index
                    # corresponds to which type
                    message_contents += PROTO_CONFIG_ENTRY.format(
                        name=included_config.split(".")[0], count=entry_count)
                    entry_count += 1

            # generate parameters
            if "parameters" in config_definition:
                for param_entry in config_definition["parameters"]:
                    for param_type, param_definition in param_entry.items():
                        message_contents += "".join(
                            PROTO_PARAM_ENTRY.format(
                                type=type_map.PROTO_TYPE_MAP[param_type],
                                name=param_definition["name"],
                                count=entry_count,
                            ))
                        entry_count += 1

            # append to output
            output_proto_contents += PROTO_MESSAGE_DEFINITION.format(
                name=name,
                contents=message_contents,
            )

        # make the top level config
        top_level_config_contents = ""
        entry_count = 1

        for include in list(set(list_of_includes)):
            top_level_config_contents += PROTO_CONFIG_ENTRY.format(
                name=include.split(".")[0], count=entry_count)
            entry_count += 1

        output_proto_contents += PROTO_MESSAGE_DEFINITION.format(
            name=top_level_proto, contents=top_level_config_contents)

        # write the output
        with open(f"{output_proto}", "w") as proto_file:
            proto_file.write(
                CONFIG_PROTO.format(
                    autogen_warning=AUTOGEN_WARNING,
                    contents=output_proto_contents,
                ))