def generate_code(named_vector_filename, include_prefix=None, vector_hh_filename=None, vector_cc_filename=None, translator_hh_filename=None, translator_cc_filename=None, lcm_filename=None): cxx_include_path = os.path.dirname(named_vector_filename) + "/gen" if cxx_include_path.startswith("external/"): # Drake is being used from within a different workspace, so we have to # strip the "external/drake/" from the named_vector_filename. (The # name after "external" will vary depending on what name the workspace # gave us, so we can't hard-code it to "drake".) cxx_include_path = "/".join(cxx_include_path.split("/")[2:]) if include_prefix: cxx_include_path = os.path.join(include_prefix, cxx_include_path) snake, _ = os.path.splitext(os.path.basename(named_vector_filename)) screaming_snake = snake.upper() camel = "".join([x.capitalize() for x in snake.split("_")]) # Load the vector's details from protobuf. # In the future, this can be extended for nested messages. with open(named_vector_filename, "r") as f: vec = named_vector_pb2.NamedVector() google.protobuf.text_format.Merge(f.read(), vec) fields = [{ 'name': el.name, 'doc': el.doc, 'default_value': el.default_value, 'doc_units': el.doc_units, 'min_value': el.min_value, 'max_value': el.max_value, } for el in vec.element] if vec.namespace: namespace_list = vec.namespace.split("::") else: namespace_list = [] # Default some field attributes if they are missing. for item in fields: if len(item['default_value']) == 0: print("error: a default_value for {}.{} is required".format( snake, item['name'])) if len(item['doc_units']) == 0: item['doc_units'] = DEFAULT_CTOR_FIELD_UNKNOWN_DOC_UNITS # The C++ namespace open & close dance is as requested in the protobuf. opening_namespace = "".join( ["namespace " + x + "{\n" for x in namespace_list]) closing_namespace = "".join( ["} // namespace " + x + "\n" for x in reversed(namespace_list)]) # The context provides string substitutions for the C++ code blocks in the # literal strings throughout this program. context = dict() context.update(cxx_include_path=cxx_include_path) context.update(camel=camel) context.update(indices=camel + 'Indices') context.update(snake=snake) context.update(screaming_snake=screaming_snake) context.update(opening_namespace=opening_namespace) context.update(closing_namespace=closing_namespace) context.update(lcm_package="drake") # This is a specially-formatted code block to warn users not to edit. # This disclaimer text is special-cased by our review tool, reviewable.io. disclaimer = "// GENERATED FILE " + "DO NOT EDIT" context.update(generated_code_warning='\n'.join( [disclaimer, "// See drake/tools/lcm_vector_gen.py."])) cxx_names = [] if vector_hh_filename: with open(vector_hh_filename, 'w') as hh: cxx_names.append(hh.name) put(hh, VECTOR_HH_PREAMBLE % context, 2) generate_indices(hh, context, fields) put(hh, '', 1) generate_indices_names_accessor_decl(hh, context) put(hh, VECTOR_CLASS_BEGIN % context, 2) generate_default_ctor(hh, context, fields) generate_set_to_named_variables(hh, context, fields) generate_do_clone(hh, context, fields) generate_accessors(hh, context, fields) put(hh, GET_COORDINATE_NAMES % context, 2) generate_is_valid(hh, context, fields) generate_calc_inequality_constraint(hh, context, fields) put(hh, VECTOR_CLASS_END % context, 2) put(hh, VECTOR_HH_POSTAMBLE % context, 1) if vector_cc_filename: with open(vector_cc_filename, 'w') as cc: cxx_names.append(cc.name) put(cc, VECTOR_CC_PREAMBLE % context, 2) generate_indices_storage(cc, context, fields) put(cc, '', 1) generate_indices_names_accessor_impl(cc, context, fields) put(cc, VECTOR_CC_POSTAMBLE % context, 1) if translator_hh_filename: with open(translator_hh_filename, 'w') as hh: cxx_names.append(hh.name) put(hh, TRANSLATOR_HH_PREAMBLE % context, 2) put(hh, TRANSLATOR_CLASS_DECL % context, 2) put(hh, TRANSLATOR_HH_POSTAMBLE % context, 1) if translator_cc_filename: with open(translator_cc_filename, 'w') as cc: cxx_names.append(cc.name) put(cc, TRANSLATOR_CC_PREAMBLE % context, 2) generate_allocate_output_vector(cc, context, fields) generate_deserialize(cc, context, fields) generate_serialize(cc, context, fields) put(cc, TRANSLATOR_CC_POSTAMBLE % context, 1) if lcm_filename: with open(lcm_filename, 'w') as lcm: put(lcm, LCMTYPE_PREAMBLE % context, 2) for field in fields: put(lcm, " double {}; // {}".format(field['name'], field['doc']), 1) put(lcm, LCMTYPE_POSTAMBLE % context, 1) if cxx_names: # Run clang-format over all C++ files. Inserting a .clang-format # settings file is problematic when formatting within bazel-genfiles, # so instead we pass its contents on the command line. with open(find_data(".clang-format"), "r") as f: yaml_data = yaml.load(f, Loader=yaml.Loader) style = str(yaml_data) # For some reason, clang-format really wants lowercase booleans. style = style.replace("False", "false").replace("True", "true") subprocess.check_call( [get_clang_format_path(), "--style=" + style, "-i"] + cxx_names)
def generate_code( named_vector_filename, vector_hh_filename=None, vector_cc_filename=None, translator_hh_filename=None, translator_cc_filename=None, lcm_filename=None): cxx_include_path = os.path.dirname(named_vector_filename) + "/gen" # TODO(#6996) Do this unconditionally once #6996 shuffle is finished. if not cxx_include_path.startswith("drake/"): # TODO(jwnimmer-tri) For use outside of Drake, this include_prefix # should probably be configurable, instead of hard-coded here. cxx_include_path = "drake/" + cxx_include_path snake, _ = os.path.splitext(os.path.basename(named_vector_filename)) screaming_snake = snake.upper() camel = "".join([x.capitalize() for x in snake.split("_")]) # Load the vector's details from protobuf. # In the future, this can be extended for nested messages. with open(named_vector_filename, "r") as f: vec = named_vector_pb2.NamedVector() google.protobuf.text_format.Merge(f.read(), vec) fields = [{ 'name': el.name, 'doc': el.doc, 'default_value': el.default_value, 'doc_units': el.doc_units, 'min_value': el.min_value, 'max_value': el.max_value, } for el in vec.element] if vec.namespace: namespace_list = vec.namespace.split("::") else: namespace_list = [] # Default some field attributes if they are missing. for item in fields: if len(item['default_value']) == 0: item['default_value'] = DEFAULT_CTOR_FIELD_DEFAULT_VALUE if len(item['doc_units']) == 0: item['doc_units'] = DEFAULT_CTOR_FIELD_UNKNOWN_DOC_UNITS # The C++ namespace open & close dance is as requested in the protobuf. opening_namespace = "".join(["namespace " + x + "{\n" for x in namespace_list]) closing_namespace = "".join(["} // namespace " + x + "\n" for x in reversed(namespace_list)]) # The context provides string substitutions for the C++ code blocks in the # literal strings throughout this program. context = dict() context.update(cxx_include_path=cxx_include_path) context.update(camel=camel) context.update(indices=camel + 'Indices') context.update(snake=snake) context.update(screaming_snake=screaming_snake) context.update(opening_namespace=opening_namespace) context.update(closing_namespace=closing_namespace) context.update(lcm_package="drake") # This is a specially-formatted code block to warn users not to edit. # This disclaimer text is special-cased by our review tool, reviewable.io. disclaimer = "// GENERATED FILE " + "DO NOT EDIT" context.update(generated_code_warning='\n'.join([ disclaimer, "// See drake/tools/lcm_vector_gen.py."])) cxx_names = [] if vector_hh_filename: with open(vector_hh_filename, 'w') as hh: cxx_names.append(hh.name) put(hh, VECTOR_HH_PREAMBLE % context, 2) generate_indices(hh, context, fields) put(hh, '', 1) generate_indices_names_accessor_decl(hh, context) put(hh, VECTOR_CLASS_BEGIN % context, 2) generate_default_ctor(hh, context, fields) generate_do_clone(hh, context, fields) generate_accessors(hh, context, fields) put(hh, GET_COORDINATE_NAMES % context, 2) generate_is_valid(hh, context, fields) generate_calc_inequality_constraint(hh, context, fields) put(hh, VECTOR_CLASS_END % context, 2) put(hh, VECTOR_HH_POSTAMBLE % context, 1) if vector_cc_filename: with open(vector_cc_filename, 'w') as cc: cxx_names.append(cc.name) put(cc, VECTOR_CC_PREAMBLE % context, 2) generate_indices_storage(cc, context, fields) put(cc, '', 1) generate_indices_names_accessor_impl(cc, context, fields) put(cc, VECTOR_CC_POSTAMBLE % context, 1) if translator_hh_filename: with open(translator_hh_filename, 'w') as hh: cxx_names.append(hh.name) put(hh, TRANSLATOR_HH_PREAMBLE % context, 2) put(hh, TRANSLATOR_CLASS_DECL % context, 2) put(hh, TRANSLATOR_HH_POSTAMBLE % context, 1) if translator_cc_filename: with open(translator_cc_filename, 'w') as cc: cxx_names.append(cc.name) put(cc, TRANSLATOR_CC_PREAMBLE % context, 2) generate_allocate_output_vector(cc, context, fields) generate_deserialize(cc, context, fields) generate_serialize(cc, context, fields) put(cc, TRANSLATOR_CC_POSTAMBLE % context, 1) if lcm_filename: with open(lcm_filename, 'w') as lcm: put(lcm, LCMTYPE_PREAMBLE % context, 2) for field in fields: put(lcm, " double {}; // {}".format(field['name'], field['doc']), 1) put(lcm, LCMTYPE_POSTAMBLE % context, 1) # Run clang-format over all C++ files. for one_filename in cxx_names: # The clang-format tool has no way to specify a config file, other than # putting a dotfile somehwere in a parent dir of the target. Because # our output is in genfiles but the dotfile is in runfiles, we won't # automatically find it. We'll resolve that by temporarily symlinking # the dotfile into place. dotfile = find_data(".clang-format") temp_dotfile = os.path.join( os.path.dirname(one_filename), ".clang-format") assert not os.path.exists(temp_dotfile) os.symlink(dotfile, temp_dotfile) subprocess.check_call([ get_clang_format_path(), "--style=file", "-i", one_filename]) os.unlink(temp_dotfile)