From 78146c1db9961b1242dc1d272749cd83ca125892 Mon Sep 17 00:00:00 2001 From: Michael Carroll Date: Wed, 11 Oct 2023 18:50:01 +0000 Subject: [PATCH] Harmonic bazel Signed-off-by: Michael Carroll --- BUILD.bazel | 116 ++++++++++----- core/generator_lite/Generator.cc | 116 +++++++++++++++ core/generator_lite/Generator.hh | 57 ++++++++ core/generator_lite/generator_main.cc | 42 ++++++ core/src/impl/InstallationDirectories.cc | 27 ++++ tools/BUILD.bazel | 13 +- tools/gz_msgs_generate.bzl | 178 ++++++++++++++++++----- tools/gz_msgs_generate.py | 174 ++++++++++++---------- tools/gz_msgs_generate_factory.py | 91 +++++++----- 9 files changed, 632 insertions(+), 182 deletions(-) create mode 100644 core/generator_lite/Generator.cc create mode 100644 core/generator_lite/Generator.hh create mode 100644 core/generator_lite/generator_main.cc create mode 100644 core/src/impl/InstallationDirectories.cc diff --git a/BUILD.bazel b/BUILD.bazel index 9e8e3c84..6c8762f4 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -9,8 +9,8 @@ load( ) load( "@gz//msgs/tools:gz_msgs_generate.bzl", - "get_proto_headers", - "gz_msgs_generate", + "gz_proto_library", + "gz_proto_factory", ) package( @@ -24,46 +24,38 @@ exports_files(["LICENSE"]) gz_configure_header( name = "msgs_config_hh", - src = "include/gz/msgs/config.hh.in", + src = "core/include/gz/msgs/config.hh.in", cmakelists = ["CMakeLists.txt"], package = "msgs", ) gz_export_header( - name = "include/gz/msgs/Export.hh", + name = "core/include/gz/msgs/Export.hh", export_base = "GZ_MSGS", lib_name = "gz-msgs", visibility = ["//visibility:private"], ) public_headers_no_gen = glob([ - "include/gz/msgs/*.hh", - "include/gz/msgs/detail/*.hh", + "core/include/gz/msgs/*.hh", + "core/include/gz/msgs/convert/*.hh", + "core/include/gz/msgs/detail/*.hh", ]) protos = glob(["proto/gz/msgs/*.proto"]) -gz_include_header( - name = "messagetypes_hh_genrule", - out = "include/gz/msgs/MessageTypes.hh", - hdrs = get_proto_headers(protos), - strip_prefix = ["gz_msgs"], -) - gz_include_header( name = "msgs_hh_genrule", out = "include/gz/msgs.hh", hdrs = public_headers_no_gen + [ - "include/gz/msgs/config.hh", - "include/gz/msgs/Export.hh", - "include/gz/msgs/MessageTypes.hh", + "core/include/gz/msgs/config.hh", + "core/include/gz/msgs/Export.hh", ], ) public_headers = public_headers_no_gen + [ - "include/gz/msgs/config.hh", - "include/gz/msgs/Export.hh", - "include/gz/msgs/MessageTypes.hh", + "core/include/gz/msgs/config.hh", + "core/include/gz/msgs/Export.hh", "include/gz/msgs.hh", ] @@ -71,9 +63,9 @@ public_headers = public_headers_no_gen + [ cc_binary( name = "gz_msgs_gen", srcs = [ - "src/Generator.cc", - "src/Generator.hh", - "src/generator_main.cc", + "core/generator_lite/Generator.cc", + "core/generator_lite/Generator.hh", + "core/generator_lite/generator_main.cc", ], deps = [ "@com_google_protobuf//:protobuf", @@ -91,26 +83,37 @@ proto_library( ], ) -gz_msgs_generate( - name = "gzmsgs_cc_proto", - deps = [ - ":gzmsgs_proto", - "@com_google_protobuf//:any_proto", - ], +gz_proto_library( + name = "gzmsgs_cc_proto", + proto_deps = [":gzmsgs_proto"], + deps = [ + "@com_google_protobuf//:protobuf", + ] +) + +gz_proto_factory( + name = "gzmsgs_proto_factory", + deps = [":gzmsgs_proto"], + namespace = "gz::msgs", + cc_output = "core/src/RegisterMsgs.cc", + hh_output = "core/src/MessageTypes.hh" ) cc_library( name = "msgs", srcs = [ - "src/Factory.cc", - "src/Filesystem.cc", - "src/Utility.cc", - ":gzmsgs_cc_proto", + "core/src/Factory.cc", + "core/src/DynamicFactory.cc", + "core/src/DynamicFactory.hh", + "core/src/MessageFactory.cc", + "core/src/impl/InstallationDirectories.cc", + "core/src/RegisterMsgs.cc" ], hdrs = public_headers, - includes = ["include"], + includes = ["core/include", "core/src"], deps = [ ":gzmsgs_cc_proto", + ":gzmsgs_proto_factory", GZ_ROOT + "math", "@com_google_protobuf//:protobuf", "@tinyxml2", @@ -138,3 +141,50 @@ test_sources = glob( "@gtest//:gtest_main", ], ) for src in test_sources] + + +cc_test( + name = "INTEGRATION_headers", + srcs = ["test/integration/headers.cc"], + deps = [ + ":gzmsgs_cc_proto", + "@gtest", + "@gtest//:gtest_main", + ], +) + +cc_test( + name = "INTEGRATION_image_msg", + srcs = ["test/integration/image_msg.cc"], + deps = [ + ":gzmsgs_cc_proto", + "@gtest", + "@gtest//:gtest_main", + ], +) + +cc_test( + name = "INTEGRATION_Utility", + srcs = ["test/integration/Utility_TEST.cc"], + deps = [ + ":msgs", + GZ_ROOT + "common/testing", + "@gtest", + "@gtest//:gtest_main", + ], +) + +cc_test( + name = "INTEGRATION_Factory", + srcs = ["test/integration/Factory_TEST.cc"], + data = ["test/desc/stringmsg.desc"], + deps = [ + ":msgs", + GZ_ROOT + "common/testing", + "@gtest", + "@gtest//:gtest_main", + ], + defines = [ + 'GZ_MSGS_TEST_PATH=\\"msgs/test\\"', + ], +) diff --git a/core/generator_lite/Generator.cc b/core/generator_lite/Generator.cc new file mode 100644 index 00000000..1279fac6 --- /dev/null +++ b/core/generator_lite/Generator.cc @@ -0,0 +1,116 @@ +/* + * Copyright 2016 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4100 4512 4127 4068 4244 4267 4251 4146) +#endif + +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "Generator.hh" + +namespace google::protobuf::compiler::cpp { + +///////////////////////////////////////////////// +Generator::Generator(const std::string &/*_name*/) +{ +} + +///////////////////////////////////////////////// +Generator::~Generator() = default; + +///////////////////////////////////////////////// +bool Generator::Generate(const FileDescriptor *_file, + const std::string &/*_parameter*/, + OutputDirectory *_generatorContext, + std::string * /*_error*/) const +{ + std::string delim = ".proto"; + auto headerFilename = _file->name(); + auto sourceFilename = _file->name(); + + { + auto pos = headerFilename.rfind(delim); + headerFilename.replace(pos, delim.size(), ".pb.h"); + } + { + auto pos = sourceFilename.rfind(delim); + sourceFilename.replace(pos, delim.size(), ".pb.h"); + } + + { + auto *output = _generatorContext->OpenForInsert(headerFilename, "includes"); + auto printer = io::Printer(output, '$'); + printer.Print("#include \n", "name", "includes"); + } + + { + auto *output = _generatorContext->OpenForInsert( + headerFilename, "namespace_scope"); + auto printer = io::Printer(output, '$'); + + for (auto i = 0; i < _file->message_type_count(); ++i) + { + const auto *desc = _file->message_type(i); + std::string ptrTypes; + + // Define std::unique_ptr types for our messages + ptrTypes += "typedef std::unique_ptr<" + + desc->name() + "> " + + desc->name() + "UniquePtr;\n"; + + // Define const std::unique_ptr types for our messages + ptrTypes += "typedef std::unique_ptrname() + "> Const" + + desc->name() + "UniquePtr;\n"; + + // Define std::shared_ptr types for our messages + ptrTypes += "typedef std::shared_ptr<" + + desc->name() + "> " + + desc->name() + "SharedPtr;\n"; + + // Define const std::shared_ptr types for our messages + ptrTypes += "typedef std::shared_ptrname() + "> Const" + + desc->name() + "SharedPtr;\n"; + + printer.PrintRaw(ptrTypes.c_str()); + } + } + return true; +} +} // namespace google::protobuf::compiler::cpp diff --git a/core/generator_lite/Generator.hh b/core/generator_lite/Generator.hh new file mode 100644 index 00000000..4630b20f --- /dev/null +++ b/core/generator_lite/Generator.hh @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2016 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + +#ifndef GZ_MSGS_GENERATOR_HH_ +#define GZ_MSGS_GENERATOR_HH_ + +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace cpp { +/// \cond +/// \brief Google protobuf message generator for igntion::msgs +class Generator : public CodeGenerator +{ + /// \brief Constructor + /// \param[in] _name Name value (currently unused) + public: explicit Generator(const std::string &_name); + + /// \brief Destructor + public: virtual ~Generator(); + + /// \brief Generate a message. + /// \param[in] _file File descriptor of the message. + /// \param[in] _parameter Unused string value + /// \param[in] _generatorContext Output directory. + /// \param[in] _error Unused string value + /// \return true if successful, otherwise sets _error and returns false + public: virtual bool Generate(const FileDescriptor *_file, + const std::string &_parameter, + OutputDirectory *_generatorContext, + std::string *_error) const; + + // private: GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Generator); +}; +/// \endcond +} +} +} +} +#endif diff --git a/core/generator_lite/generator_main.cc b/core/generator_lite/generator_main.cc new file mode 100644 index 00000000..79008fb6 --- /dev/null +++ b/core/generator_lite/generator_main.cc @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2016 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4251) +#endif + +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "Generator.hh" + +int main(int _argc, char *_argv[]) +{ +#ifdef _MSC_VER + // Don't print a silly message or stick a modal dialog box in my face, + // please. + _set_abort_behavior(0, ~0); +#endif // !_MSC_VER + + google::protobuf::compiler::cpp::Generator + generator("gz-msgs-plugin"); + return google::protobuf::compiler::PluginMain(_argc, _argv, &generator); +} diff --git a/core/src/impl/InstallationDirectories.cc b/core/src/impl/InstallationDirectories.cc new file mode 100644 index 00000000..7f25a895 --- /dev/null +++ b/core/src/impl/InstallationDirectories.cc @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2023 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +namespace gz::msgs +{ +inline namespace GZ_MSGS_VERSION_NAMESPACE { +std::string getInstallPrefix() { return {};} +} // namespcae +} // namespace gz::msgs diff --git a/tools/BUILD.bazel b/tools/BUILD.bazel index 8bdd14a8..3568652b 100644 --- a/tools/BUILD.bazel +++ b/tools/BUILD.bazel @@ -1,7 +1,12 @@ -load("@gz//bazel/skylark:gz_py.bzl", "gz_py_binary") +load( + "@gz//bazel/skylark:build_defs.bzl", + "GZ_VISIBILITY", + "gz_py_binary", +) gz_py_binary( - name = "gz_msgs_generate_py", - srcs = ["gz_msgs_generate.py"], - visibility = ["//visibility:public"], + name = "gz_msgs_generate_factory_py", + srcs = ["gz_msgs_generate_factory.py"], + main = "gz_msgs_generate_factory.py", + visibility = GZ_VISIBILITY, ) diff --git a/tools/gz_msgs_generate.bzl b/tools/gz_msgs_generate.bzl index b28024e8..80862211 100644 --- a/tools/gz_msgs_generate.bzl +++ b/tools/gz_msgs_generate.bzl @@ -9,11 +9,29 @@ load( _VIRTUAL_IMPORTS = "/_virtual_imports/" -def _get_detail_directory(hh_file, ctx): - base = hh_file.path[hh_file.path.index(_VIRTUAL_IMPORTS) + 1:] - base = base.split("/") - base.insert(-1, "details") - return ctx.actions.declare_file("/".join(base)) +def _filter_files_impl(ctx): + """Filter the files in DefaultInfo.""" + return [DefaultInfo( + files = depset([ + file + for file in ctx.attr.target.files.to_list() + if file.extension in ctx.attr.extensions + ]), + )] + +filter_files = rule( + implementation = _filter_files_impl, + attrs = { + "target": attr.label( + doc = "The source target to filter", + mandatory = True, + ), + "extensions": attr.string_list( + doc = "The extensions of the files to keep eg. ['h']", + mandatory = True, + ), + }, +) def get_proto_headers(protos): out = [] @@ -23,42 +41,127 @@ def get_proto_headers(protos): out.append("/".join(split)) return out -def _gz_msgs_generate_impl(ctx): +def _gz_proto_factory_impl(ctx): protos = protos_from_context(ctx) out_dir = get_out_dir(protos, ctx) - + in_protos = [] include_dirs = depset([get_include_directory(proto) for proto in protos]) + for src in ctx.attr.deps: + for proto in src[ProtoInfo].direct_sources: + in_protos.append(proto) + + for proto in src[ProtoInfo].transitive_sources.to_list(): + in_protos.append(proto) + + in_protos = depset(in_protos).to_list() arguments = [ - "--protoc-exec=" + ctx.executable._protoc.path, - "--gz-generator-bin=" + ctx.executable._gz_gen_bin.path, - "--generate-cpp", - "--output-cpp-path=" + out_dir.path, + "--cc-output=" + ctx.outputs.cc_output.path, + "--hh-output=" + ctx.outputs.hh_output.path ] - for proto in protos: - arguments.append("--input-path=" + proto.path) - for include_dir in include_dirs.to_list(): arguments.append("--proto-path=" + include_dir) - out_protos = [proto for proto in protos if proto.path.find("gz/msgs") > 0] + arguments.append("--proto-include-path=" + out_dir.path) + arguments.append("--namespace=" + ctx.attr.namespace) + arguments.append("--protos") + for proto in in_protos: + arguments.append(proto.path) + + ctx.actions.run( + inputs = protos, + outputs = [ctx.outputs.cc_output, ctx.outputs.hh_output], + arguments = arguments, + executable = ctx.executable.gz_msgs_generate_factory_py, + ) + + compilation_context = cc_common.create_compilation_context( + headers = depset([ctx.outputs.hh_output]), + system_includes = depset([ctx.outputs.hh_output.dirname]), + ) + + return [ + CcInfo(compilation_context = compilation_context), + ] + +_gz_proto_factory = rule( + attrs = { + "deps": attr.label_list( + mandatory = True, + allow_empty = False, + providers = [ProtoInfo], + ), + "namespace": attr.string( + mandatory = True, + ), + "cc_output": attr.output(mandatory = True), + "hh_output": attr.output(mandatory = True), + "gz_msgs_generate_factory_py": attr.label( + default = Label("@gz//msgs/tools:gz_msgs_generate_factory_py"), + executable = True, + cfg = "host", + ), + }, + implementation = _gz_proto_factory_impl, +) + +def gz_proto_factory( + deps, + **kwargs): + _gz_proto_factory( + deps = deps, + **kwargs + ) + +def _gz_proto_library_impl(ctx): + protos = protos_from_context(ctx) + out_dir = get_out_dir(protos, ctx) + + arguments = [] + + in_protos = [] + out_protos = [] + include_dirs = [] + + for src in ctx.attr.deps: + for proto in src[ProtoInfo].direct_sources: + in_protos.append(proto) + include_dirs.append(get_include_directory(proto)) + out_protos.append(proto) + + for proto in src[ProtoInfo].transitive_sources.to_list(): + in_protos.append(proto) + include_dirs.append(get_include_directory(proto)) + + in_protos = depset(in_protos).to_list() + out_protos = depset(out_protos).to_list() + include_dirs = depset(include_dirs).to_list() + + for include_dir in include_dirs: + arguments.append("--proto_path=" + include_dir) + + arguments.append("--plugin=protoc-gen-gzmsgs=" + ctx.executable._gz_gen_bin.path) + arguments.append("--cpp_out=" + out_dir.path) + arguments.append("--gzmsgs_out=" + out_dir.path) + + for proto in in_protos: + arguments.append(proto.path) cc_files = declare_out_files(out_protos, ctx, "{}.pb.cc") hh_files = declare_out_files(out_protos, ctx, "{}.pb.h") - detail_hh_files = [_get_detail_directory(file, ctx) for file in hh_files] - out_files = cc_files + hh_files + detail_hh_files + out_files = cc_files + hh_files ctx.actions.run( - inputs = protos, + inputs = in_protos, outputs = out_files, arguments = arguments, - executable = ctx.executable.gz_msgs_generate_py, - tools = [ctx.executable._protoc, ctx.executable._gz_gen_bin, ctx.executable.gz_msgs_generate_py], + executable = ctx.executable._protoc, + tools = [ctx.executable._protoc, ctx.executable._gz_gen_bin], ) compilation_context = cc_common.create_compilation_context( - headers = depset(out_files), + headers = depset(hh_files), system_includes = depset([out_dir.path]), ) @@ -67,7 +170,7 @@ def _gz_msgs_generate_impl(ctx): CcInfo(compilation_context = compilation_context), ] -_gz_msgs_generate_gen = rule( +_gz_proto_library = rule( attrs = { "deps": attr.label_list( mandatory = True, @@ -84,19 +187,24 @@ _gz_msgs_generate_gen = rule( executable = True, cfg = "host", ), - "gz_msgs_generate_py": attr.label( - default = Label("@gz//msgs/tools:gz_msgs_generate_py"), - executable = True, - cfg = "host", - ), }, - implementation = _gz_msgs_generate_impl, + implementation = _gz_proto_library_impl, ) -def gz_msgs_generate( - deps, - **kwargs): - _gz_msgs_generate_gen( - deps = deps, - **kwargs - ) +def gz_proto_library( + name, + proto_deps, + **kwargs): + + _gz_proto_library(name = name + "_pb", deps = proto_deps) + + filter_files(name=name + "_srcs", target=":" + name + "_pb", extensions=["cc"]) + filter_files(name=name + "_hdrs", target=":" + name + "_pb", extensions=["h"]) + kwargs['deps'].append(":" + name + "_pb") + + native.cc_library( + name = name, + srcs = [name + "_srcs"], + hdrs = [name + "_hdrs"], + **kwargs + ) diff --git a/tools/gz_msgs_generate.py b/tools/gz_msgs_generate.py index 1805e2f3..6b755a7d 100755 --- a/tools/gz_msgs_generate.py +++ b/tools/gz_msgs_generate.py @@ -35,14 +35,18 @@ def main(argv=sys.argv[1:]): help='Flag to indicate if C++ bindings should be generated', action='store_true') parser.add_argument( - '--generate-python', - help='Flag to indicate if Python bindings should be generated', + '--generate-ruby', + help='Flag to indicate if Ruby bindings should be generated', + action='store_true') + parser.add_argument( + '--generate-ignition', + help='Flag to indicate if ignition/ headers should be generated', action='store_true') parser.add_argument( '--output-cpp-path', help='The basepath of the generated C++ files') parser.add_argument( - '--output-python-path', + '--output-ruby-path', help='The basepath of the generated C++ files') parser.add_argument( '--proto-path', @@ -52,79 +56,101 @@ def main(argv=sys.argv[1:]): parser.add_argument( '--input-path', required=True, - help='The location of the template files') - parser.add_argument( - '--dependency-proto-descs', - nargs='*', - help='The location of proto descriptor files these messages depend on') - parser.add_argument( - '--dllexport-decl', - help='The DLL visibility macro to use, if not set, no macro will be used') + help='The location of the template files', + action='append') args = parser.parse_args(argv) - # First generate the base cpp files - cmd = [args.protoc_exec] - - for path in args.proto_path: - cmd += [f'--proto_path={path}'] - - if args.dependency_proto_descs: - for path in args.dependency_proto_descs: - cmd += [f'--descriptor_set_in={path}'] - - if args.generate_cpp: - cmd += [f'--plugin=protoc-gen-gzmsgs={args.gz_generator_bin}'] - if args.dllexport_decl: - cmd += [f'--cpp_out=dllexport_decl={args.dllexport_decl}:{args.output_cpp_path}'] - else: - cmd += [f'--cpp_out={args.output_cpp_path}'] - - cmd += [f'--gzmsgs_out={args.output_cpp_path}'] - if args.generate_python: - cmd += [f'--python_out={args.output_python_path}'] - - cmd += [args.input_path] - - try: - subprocess.check_call(cmd) - except subprocess.CalledProcessError as e: - print(f'Failed to execute protoc compiler: {e}') - sys.exit(-1) - - os.makedirs(args.output_cpp_path, exist_ok=True) - - try: - subprocess.check_call(cmd) - except subprocess.CalledProcessError as e: - print(f'Failed to execute protoc compiler: {e}') - sys.exit(-1) - - # Move original generated cpp to details/ - proto_file = os.path.splitext(os.path.relpath(args.input_path, args.proto_path[0]))[0] - detail_proto_file = proto_file.split(os.sep) - - detail_proto_dir = detail_proto_file[:-1] - detail_proto_dir.append('details') - detail_proto_dir = os.path.join(*detail_proto_dir) - detail_proto_file.insert(-1, 'details') - detail_proto_file = os.path.join(*detail_proto_file) - - header = os.path.join(args.output_cpp_path, proto_file + ".pb.h") - gz_header = os.path.join(args.output_cpp_path, proto_file + ".gz.h") - detail_header = os.path.join(args.output_cpp_path, detail_proto_file + ".pb.h") - - try: - os.makedirs(os.path.join(args.output_cpp_path, detail_proto_dir), - exist_ok=True) - # Windows cannot rename a file to an existing file - if os.path.exists(detail_header): - os.remove(detail_header) - - os.rename(header, detail_header) - os.rename(gz_header, header) - except Exception as e: - print(f'Failed to manipulate gz-msgs headers: {e}') - sys.exit(-1) + for input_file in args.input_path: + # First generate the base cpp and ruby files + cmd = [args.protoc_exec] + + for pp in args.proto_path: + cmd += [f'--proto_path={pp}'] + + if args.generate_cpp: + cmd += [f'--plugin=protoc-gen-ignmsgs={args.gz_generator_bin}'] + cmd += [f'--cpp_out=dllexport_decl=GZ_MSGS_VISIBLE:{args.output_cpp_path}'] + cmd += [f'--ignmsgs_out={args.output_cpp_path}'] + if args.generate_ruby: + cmd += [f'--ruby_out=dllexport_decl=GZ_MSGS_VISIBLE:{args.output_ruby_path}'] + cmd += [input_file] + + try: + subprocess.check_call(cmd) + except subprocess.CalledProcessError as e: + print(f'Failed to execute protoc compiler: {e}') + sys.exit(-1) + + # Move original generated cpp to details/ + proto_file = None + for proto_path in args.proto_path: + if input_file.find(proto_path) == 0: + proto_file = os.path.splitext(os.path.relpath(input_file, proto_path))[0] + if not proto_file: + continue + + detail_proto_file = proto_file.split(os.sep) + + detail_proto_dir = detail_proto_file[:-1] + detail_proto_dir.append('details') + detail_proto_dir = os.path.join(*detail_proto_dir) + detail_proto_file.insert(-1, 'details') + detail_proto_file = os.path.join(*detail_proto_file) + + header = os.path.join(args.output_cpp_path, proto_file + ".pb.h") + gz_header = os.path.join(args.output_cpp_path, proto_file + ".gz.h") + detail_header = os.path.join(args.output_cpp_path, detail_proto_file + ".pb.h") + + if proto_file.find('google/protobuf') >= 0: + continue + + try: + os.makedirs(os.path.join(args.output_cpp_path, detail_proto_dir), + exist_ok=True) + # Windows cannot rename a file to an existing file + if os.path.exists(detail_header): + os.remove(detail_header) + + os.rename(header, detail_header) + os.rename(gz_header, header) + except Exception as e: + print(f'Failed to manipulate gz-msgs headers: {e}') + sys.exit(-1) + + + if args.generate_ignition: + ignition_header_dir = os.path.join(args.output_cpp_path, 'ignition', 'msgs') + ignition_header = proto_file.split(os.sep) + ignition_header[0] = 'ignition' + + proto_name = ignition_header[2] + + ignition_header = os.path.join(*ignition_header) + ignition_header = os.path.join(args.output_cpp_path, ignition_header + ".pb.h") + + os.makedirs(os.path.join(args.output_cpp_path, ignition_header_dir), + exist_ok=True) + + with open(ignition_header, 'w') as f: + f.write('''/* + * Copyright (C) 2022 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + ''') + f.write(f'#include \n') + f.write('#include \n') if __name__ == '__main__': sys.exit(main()) diff --git a/tools/gz_msgs_generate_factory.py b/tools/gz_msgs_generate_factory.py index 89553422..4fa8002e 100755 --- a/tools/gz_msgs_generate_factory.py +++ b/tools/gz_msgs_generate_factory.py @@ -16,7 +16,7 @@ import argparse import os -import pathlib +import re import sys # Create @@ -72,9 +72,10 @@ * Do not edit this directly */ +#include "MessageTypes.hh" + #include "gz/msgs/Factory.hh" #include "gz/msgs/MessageFactory.hh" -#include "{package_path}/MessageTypes.hh" #include @@ -110,17 +111,25 @@ def main(argv=sys.argv[1:]): description='Generate protobuf factory file', formatter_class=argparse.ArgumentDefaultsHelpFormatter) parser.add_argument( - '--output-cpp-path', + '--cc-output', + required=True, + help='The path to the generated cpp file') + parser.add_argument( + '--hh-output', required=True, - help='The basepath of the generated C++ files') + help='The path to the generated hh file') parser.add_argument( - '--proto-package', + '--namespace', required=True, - help='The basepath of the generated C++ files') + help='The namespace to use') parser.add_argument( '--proto-path', required=True, help='The location of the protos') + parser.add_argument( + '--proto-include-path', + required=True, + help='The location of the protos') parser.add_argument( '--protos', type=str, @@ -131,45 +140,55 @@ def main(argv=sys.argv[1:]): args = parser.parse_args(argv) - headers = [] - registrations = [] + package_re = re.compile('^package (.*);$') + message_re = re.compile('message (.*)') - package = [p for p in args.proto_package.split('.') if len(p)] - namespace = '::'.join(package) - package_str = '.'.join(package) - package_path = '/'.join(package) + registrations = [] + gz_msgs_headers = [] for proto in args.protos: - proto_file = os.path.splitext(os.path.relpath(proto, args.proto_path))[0] - header = proto_file + ".pb.h" - headers.append(f"#include <{header}>") - - proto_file = '_'.join(pathlib.Path(proto_file).parts) - - # The gazebo extensions to the gazebo compiler write out a series of index files - # which capture the message types - index = os.path.join(args.output_cpp_path, proto_file + ".pb_index") - with open(index, "r") as index_f: - for line in index_f.readlines(): - line = line.strip() + package = None + messages = [] + + try: + with open(proto, 'r') as f: + content = f.readlines() + for line in content: + package_found = package_re.match(line) + if package_found: + package = package_found.group(1).split('.') + + message_found = message_re.match(line) + if message_found: + messages.append(message_found.group(1)) + except: + pass + + if package and messages: + for message in messages: + registrations.append(register_fn.format( + package_str='.'.join(package), + message_str=message, + message_cpp_type='::'.join([*package, message]) + )) - message_str = line - message_cpp_type = '::'.join(package) + '::' + message_str - registrations.append(register_fn.format( - package_str=package_str, - message_str=message_str, - message_cpp_type=message_cpp_type)) + split = proto.replace(args.proto_include_path, '') + split = [s for s in split.split("/") if s] + split[-1] = split[-1].replace(".proto", ".pb.h") + print(split) - with open(os.path.join(args.output_cpp_path, *package, 'MessageTypes.hh'), 'w') as f: - f.write(cc_header.format(gz_msgs_headers='\n'.join(headers), namespace=namespace)) + gz_msgs_headers.append("#include <" + "/".join(split) + ">") - with open(os.path.join(args.output_cpp_path, *package, 'register.cc'), 'w') as f: + with open(os.path.join(args.cc_output), 'w') as f: f.write((cc_source.format(registrations='\n'.join(registrations), nRegistrations=len(registrations), - namespace=namespace, - package_path=package_path) + - cc_factory.format(namespace=namespace))) + namespace=args.namespace) + + cc_factory.format(namespace=args.namespace))) + + with open(os.path.join(args.hh_output), 'w') as f: + f.write(cc_header.format(namespace=args.namespace, + gz_msgs_headers='\n'.join(gz_msgs_headers))) if __name__ == '__main__': sys.exit(main())