diff --git a/BUILD.bazel b/BUILD.bazel index 9e8e3c84..9a23f639 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -129,7 +129,7 @@ test_sources = glob( "test/desc", ], defines = [ - 'GZ_MSGS_TEST_PATH=\\"msgs/test\\"', + 'GZ_MSGS_TEST_PATH=\\"msgs/test\\"', ], deps = [ ":msgs", diff --git a/CMakeLists.txt b/CMakeLists.txt index d50c5c19..4b79a04d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,34 +25,49 @@ gz_configure_project(VERSION_SUFFIX #============================================================================ # Cross-compilation related options -# In a cross-compilation scenario, it is possible that the gz_msgs_gen +# In a cross-compilation scenario, it is possible that the +# ${PROJECT_NAME}_protoc_plugin # generator compiled for the target machine cannot be used to generate -# the C++ code corresponding to the .proto definition. For this scenario, -# the following two options can be used as follows. +# the C++ code corresponding to the .proto definition. +# Similarly, it is possible that Python3::Interpreter and protobuf::protoc +# found via find_package are not executable that can be run during the build. +# To avoid, that the following options can be used as follows. # First of all, gz-msgs is compiled targeting the host machine, and in the -# build targeting the host, the INSTALL_GZ_MSGS_GEN_EXECUTABLE option is -# enabled: -# > cmake -DINSTALL_GZ_MSGS_GEN_EXECUTABLE:BOOL=ON .. -# ensuring that the gz_msgs_gen is installed in -# /bin/gz_msgs_gen . Then, the same version of gz-msgs +# build targeting the host, ensuring that the ${PROJECT_NAME}_protoc_plugin +# is installed in /bin/${PROJECT_NAME}_protoc_plugin . +# Then, the same version of gz-msgs # can be cross-compiled, and in the cross-compilation build the location of the -# host gz_msgs_gen is specified via the GZ_MSGS_GEN_EXECUTABLE -# CMake cache variable: -# > cmake -GZ_MSGS_GEN_EXECUTABLE=/bin/gz_msgs_gen .. +# host gz_msgs_gen is specified via the GZ_MSGS_GEN_EXECUTABLE CMake option. +# Similarly to gz-msgs, also a copy of protoc and of the Python interpreter need +# to be installed in the host at /bin, and they can be passed +# to the build appropriate CMake options: +# > cmake -Dgz-msgs_PROTO_GENERATOR_PLUGIN=gz-msgs_protoc_plugin +# > -Dgz-msgs_PROTOC_EXECUTABLE=/bin/protoc +# > -Dgz-msgs_PYTHON_INTERPRETER=/bin/python +# > .. +# In case the gz-msgs CMake functions are used also in downstream projects, +# the same variables can be passed when configuring the downsream projects. -option( - INSTALL_GZ_MSGS_GEN_EXECUTABLE - "Install the gz_msgs_gen executable." - OFF) +set( + ${PROJECT_NAME}_PROTO_GENERATOR_PLUGIN + "${PROJECT_NAME}_protoc_plugin" + CACHE STRING + "gz_msgs_gen executable used in the gz-msgs CMake functions.") +mark_as_advanced(${PROJECT_NAME}_PROTO_GENERATOR_PLUGIN) -mark_as_advanced(INSTALL_GZ_MSGS_GEN_EXECUTABLE) +set( + ${PROJECT_NAME}_PROTOC_EXECUTABLE + protobuf::protoc + CACHE STRING + "protoc target or executable used in the gz-msgs CMake functions.") +mark_as_advanced(${PROJECT_NAME}_PROTOC_EXECUTABLE) set( - GZ_MSGS_GEN_EXECUTABLE - "$" + ${PROJECT_NAME}_PYTHON_INTERPRETER + Python3::Interpreter CACHE STRING - "gz_msgs_gen executable used in the gz_msgs_protoc CMake function.") -mark_as_advanced(GZ_MSGS_GEN_EXECUTABLE) + "python target or executable used in the gz-msgs CMake functions.") +mark_as_advanced(${PROJECT_NAME}_PYTHON_INTERPRETER) # Python interfaces vars option(USE_SYSTEM_PATHS_FOR_PYTHON_INSTALLATION @@ -92,7 +107,7 @@ find_package(Python3 REQUIRED COMPONENTS Interpreter) # Note that CLI files are installed regardless of whether the dependency is # available during build time find_program(HAVE_GZ_TOOLS gz) -set(GZ_TOOLS_VER 1) +set(GZ_TOOLS_VER 2) #-------------------------------------- # Find Tinyxml2 diff --git a/Changelog.md b/Changelog.md index caf99e83..e428cf00 100644 --- a/Changelog.md +++ b/Changelog.md @@ -4,6 +4,40 @@ ## Gazebo Msgs 10.x +### Gazebo Msgs 10.1.0 (2024-01-22) + +1. Add proto message for MaterialColor. + * [Pull request #414](https://github.com/gazebosim/gz-msgs/pull/414) + * [Pull request #415](https://github.com/gazebosim/gz-msgs/pull/415) + * [Pull request #416](https://github.com/gazebosim/gz-msgs/pull/416) + +1. Update CI badges in README + * [Pull request #412](https://github.com/gazebosim/gz-msgs/pull/412) + +1. Ensure that tests pass fine with GZ_RELOCATABLE_INSTALL option enabled + * [Pull request #394](https://github.com/gazebosim/gz-msgs/pull/394) + +1. Added missing parts to the `CMakeLists.txt` code. + * [Pull request #400](https://github.com/gazebosim/gz-msgs/pull/400) + +1. Miscellaneous cleanups of protobuf generator plugin + * [Pull request #405](https://github.com/gazebosim/gz-msgs/pull/405) + +1. Fix memory leak in generator plugin code + * [Pull request #404](https://github.com/gazebosim/gz-msgs/pull/404) + +1. Enables cmd commands on Windows + * [Pull request #402](https://github.com/gazebosim/gz-msgs/pull/402) + +1. Miscellaneous housekeeping + * [Pull request #398](https://github.com/gazebosim/gz-msgs/pull/398) + +1. Fix cross-compilation support for gz-msg10 + * [Pull request #392](https://github.com/gazebosim/gz-msgs/pull/392) + +1. Do not redefine GZ_PYTHON_INSTALL_PATH if it is already defined + * [Pull request #393](https://github.com/gazebosim/gz-msgs/pull/393) + ### Gazebo Msgs 10.0.0 (2023-09-29) 1. Add missing `` header diff --git a/README.md b/README.md index a1a02fc0..15e73331 100644 --- a/README.md +++ b/README.md @@ -9,10 +9,10 @@ Build | Status -- | -- -Test coverage | [![codecov](https://codecov.io/gh/gazebosim/gz-msgs/branch/main/graph/badge.svg)](https://codecov.io/gh/gazebosim/gz-msgs) -Ubuntu Focal | [![Build Status](https://build.osrfoundation.org/buildStatus/icon?job=ignition_msgs-ci-main-focal-amd64)](https://build.osrfoundation.org/job/ignition_msgs-ci-main-focal-amd64) -Homebrew | [![Build Status](https://build.osrfoundation.org/buildStatus/icon?job=ignition_msgs-ci-main-homebrew-amd64)](https://build.osrfoundation.org/job/ignition_msgs-ci-main-homebrew-amd64) -Windows | [![Build Status](https://build.osrfoundation.org/buildStatus/icon?job=ign_msgs-ci-win)](https://build.osrfoundation.org/job/ign_msgs-ci-win) +Test coverage | [![codecov](https://codecov.io/gh/gazebosim/gz-msgs/tree/main/graph/badge.svg)](https://codecov.io/gh/gazebosim/gz-msgs/tree/main) +Ubuntu Jammy | [![Build Status](https://build.osrfoundation.org/buildStatus/icon?job=gz_msgs-ci-main-jammy-amd64)](https://build.osrfoundation.org/job/gz_msgs-ci-main-jammy-amd64) +Homebrew | [![Build Status](https://build.osrfoundation.org/buildStatus/icon?job=gz_msgs-ci-main-homebrew-amd64)](https://build.osrfoundation.org/job/gz_msgs-ci-main-homebrew-amd64) +Windows | [![Build Status](https://build.osrfoundation.org/buildStatus/icon?job=gz_msgs-main-win)](https://build.osrfoundation.org/job/gz_msgs-main-win) Gazebo Messages is a component in the [Gazebo](http://gazebosim.org) framework, a set of libraries designed to rapidly develop robot applications. diff --git a/cmake/gz_msgs_factory.cmake b/cmake/gz_msgs_factory.cmake index b86b1b4f..2af59ffa 100644 --- a/cmake/gz_msgs_factory.cmake +++ b/cmake/gz_msgs_factory.cmake @@ -6,7 +6,7 @@ # One value arguments: # FACTORY_GEN_SCRIPT - Location of the factory generator script # PROTO_PACKAGE - Protobuf package the file belongs to (e.g. "gz.msgs") -# PROTOC_EXEC - Path to protoc +# PYTHON_INTERPRETER - Target or path to the Python interpreter to use to execute the generator script # OUTPUT_CPP_DIR - Path where C++ files are saved # OUTPUT_CPP_HH_VAR - A CMake variable name containing a list that the C++ headers should be appended to # OUTPUT_CPP_CC_VAR - A Cmake variable name containing a list that the C++ sources should be appended to @@ -18,6 +18,7 @@ function(gz_msgs_factory) set(oneValueArgs FACTORY_GEN_SCRIPT PROTO_PACKAGE + PYTHON_INTERPRETER OUTPUT_CPP_DIR OUTPUT_CPP_HH_VAR OUTPUT_CPP_CC_VAR) @@ -25,6 +26,11 @@ function(gz_msgs_factory) cmake_parse_arguments(gz_msgs_factory "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + # Default values for optional parameters + if (NOT DEFINED gz_msgs_factory_PYTHON_INTERPRETER) + set(gz_msgs_factory_PYTHON_INTERPRETER Python3::Interpreter) + endif() + _gz_msgs_proto_pkg_to_path(${gz_msgs_factory_PROTO_PACKAGE} proto_package_dir) set(output_header "${gz_msgs_factory_OUTPUT_CPP_DIR}/${proto_package_dir}/MessageTypes.hh") @@ -57,7 +63,7 @@ function(gz_msgs_factory) add_custom_command( OUTPUT ${output_files} - COMMAND Python3::Interpreter + COMMAND ${gz_msgs_factory_PYTHON_INTERPRETER} ARGS ${gz_msgs_factory_FACTORY_GEN_SCRIPT} ${GENERATE_ARGS} DEPENDS ${depends_index} diff --git a/cmake/gz_msgs_generate.cmake b/cmake/gz_msgs_generate.cmake index 679b9cae..5fc0cf3d 100644 --- a/cmake/gz_msgs_generate.cmake +++ b/cmake/gz_msgs_generate.cmake @@ -2,6 +2,7 @@ # The implementation of gz_msgs_generate_messages # Options: # One value arguments: +# PYTHON_INTERPRETER - Target or path to the python interpreter used # PROTO_PACKAGE - Protobuf package the file belongs to (e.g. "gz.msgs") # MSGS_GEN_SCRIPT - Location of the messge generator script # GZ_PROTOC_PLUGIN - Location of the gazebo generator plugin @@ -20,6 +21,8 @@ function(gz_msgs_generate_messages_impl) set(options "") set(oneValueArgs # Inputs + PYTHON_INTERPRETER + PROTOC_EXEC PROTO_PACKAGE MSGS_GEN_SCRIPT GZ_PROTOC_PLUGIN FACTORY_GEN_SCRIPT PROTO_PATH DEPENDENCY_DESCRIPTIONS DLLEXPORT_DECL @@ -33,6 +36,15 @@ function(gz_msgs_generate_messages_impl) set(multiValueArgs INPUT_PROTOS) cmake_parse_arguments(generate_messages "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + # Default values for optional parameters + if (NOT DEFINED generate_messages_PYTHON_INTERPRETER) + set(generate_messages_PYTHON_INTERPRETER Python3::Interpreter) + endif() + if (NOT DEFINED generate_messages_PROTOC_EXEC) + set(generate_messages_PROTOC_EXEC protobuf::protoc) + endif() + _gz_msgs_proto_pkg_to_string(${generate_messages_PROTO_PACKAGE} gen_dir) _gz_msgs_proto_pkg_to_path(${generate_messages_PROTO_PACKAGE} proto_package_dir) set(output_directory ${generate_messages_OUTPUT_DIRECTORY}) @@ -42,6 +54,8 @@ function(gz_msgs_generate_messages_impl) foreach(proto_file ${generate_messages_INPUT_PROTOS}) gz_msgs_protoc( + PYTHON_INTERPRETER + ${generate_messages_PYTHON_INTERPRETER} MSGS_GEN_SCRIPT ${generate_messages_MSGS_GEN_SCRIPT} PROTO_PACKAGE @@ -49,7 +63,7 @@ function(gz_msgs_generate_messages_impl) INPUT_PROTO ${proto_file} PROTOC_EXEC - protobuf::protoc + ${generate_messages_PROTOC_EXEC} GZ_PROTOC_PLUGIN ${generate_messages_GZ_PROTOC_PLUGIN} PROTO_PATH @@ -81,6 +95,8 @@ function(gz_msgs_generate_messages_impl) endforeach() gz_msgs_factory( + PYTHON_INTERPRETER + ${generate_messages_PYTHON_INTERPRETER} FACTORY_GEN_SCRIPT ${generate_messages_FACTORY_GEN_SCRIPT} PROTO_PACKAGE @@ -130,6 +146,7 @@ endfunction() ################################################## # Options: # One value arguments: +# PROTOC_EXEC - protoc target or executable to use # PROTO_PATH - Base directory of the proto files # DEPENDENCY_DESCRIPTIONS - Variable containing all depedency description files # OUTPUT_DIRECTORY - Directory of output gz_desc file @@ -140,6 +157,7 @@ function(gz_msgs_generate_desc_impl) set(options "") set(oneValueArgs # Inputs + PROTOC_EXEC PROTO_PATH DEPENDENCY_DESCRIPTIONS OUTPUT_DIRECTORY @@ -148,6 +166,10 @@ function(gz_msgs_generate_desc_impl) cmake_parse_arguments(generate_messages "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + if (NOT DEFINED generate_messages_PROTOC_EXEC) + set(generate_messages_PROTOC_EXEC protobuf::protoc) + endif() + set(ARGS) list(APPEND ARGS -I${generate_messages_PROTO_PATH}) list(APPEND ARGS --descriptor_set_out=${generate_messages_OUTPUT_FILENAME}) @@ -161,7 +183,7 @@ function(gz_msgs_generate_desc_impl) add_custom_command( OUTPUT ${generate_messages_OUTPUT_FILENAME} - COMMAND protobuf::protoc + COMMAND ${generate_messages_PROTOC_EXEC} ARGS ${ARGS} DEPENDS ${generate_messages_INPUT_PROTOS} COMMENT "Generating descriptor set" @@ -175,6 +197,8 @@ endfunction() # TARGET - Target (static library) to create # PROTO_PACKAGE - Protobuf package the file belongs to (e.g. "gz.msgs") # MSGS_GEN_SCRIPT - Location of the messge generator script +# PYTHON_INTERPRETER - Target or path to the python interpreter used +# PROTOC_EXEC - Protoc target or executable to use # GZ_PROTOC_PLUGIN - Location of the gazebo generator plugin # FACTORY_GEN_SCRIPT - Location of the factory generator script # MSGS_LIB - gz-msgs library to link to @@ -186,7 +210,7 @@ endfunction() # that depend on gz-msgs function(gz_msgs_generate_messages_lib) set(options "") - set(oneValueArgs TARGET PROTO_PACKAGE MSGS_GEN_SCRIPT GZ_PROTOC_PLUGIN FACTORY_GEN_SCRIPT MSGS_LIB PROTO_PATH) + set(oneValueArgs TARGET PROTO_PACKAGE MSGS_GEN_SCRIPT PYTHON_INTERPRETER PROTOC_EXEC GZ_PROTOC_PLUGIN FACTORY_GEN_SCRIPT MSGS_LIB PROTO_PATH) set(multiValueArgs INPUT_PROTOS DEPENDENCIES) cmake_parse_arguments(generate_messages "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) @@ -205,9 +229,19 @@ function(gz_msgs_generate_messages_lib) list(APPEND depends_msgs_desc ${msgs_desc_file}) endforeach() + # Default values for optional parameters + if (NOT DEFINED generate_messages_PYTHON_INTERPRETER) + set(generate_messages_PYTHON_INTERPRETER Python3::Interpreter) + endif() + if (NOT DEFINED generate_messages_PROTOC_EXEC) + set(generate_messages_PROTOC_EXEC protobuf::protoc) + endif() + gz_msgs_generate_messages_impl( PROTO_PACKAGE ${generate_messages_PROTO_PACKAGE} MSGS_GEN_SCRIPT ${generate_messages_MSGS_GEN_SCRIPT} + PYTHON_INTERPRETER ${generate_messages_PYTHON_INTERPRETER} + PROTOC_EXEC ${generate_messages_PROTOC_EXEC} GZ_PROTOC_PLUGIN ${generate_messages_GZ_PROTOC_PLUGIN} FACTORY_GEN_SCRIPT ${generate_messages_FACTORY_GEN_SCRIPT} PROTO_PATH ${generate_messages_PROTO_PATH} @@ -221,6 +255,7 @@ function(gz_msgs_generate_messages_lib) ) gz_msgs_generate_desc_impl( + PROTOC_EXEC ${generate_messages_PROTOC_EXEC} INPUT_PROTOS ${generate_messages_INPUT_PROTOS} PROTO_PATH ${generate_messages_PROTO_PATH} DEPENDENCY_DESCRIPTIONS ${depends_msgs_desc} diff --git a/cmake/gz_msgs_protoc.cmake b/cmake/gz_msgs_protoc.cmake index 077e266c..863d6b64 100644 --- a/cmake/gz_msgs_protoc.cmake +++ b/cmake/gz_msgs_protoc.cmake @@ -4,6 +4,7 @@ # GENERATE_CPP - generates c++ code for the message if specified # GENERATE_PYTHON - generates python code for the message if specified # One value arguments: +# PYTHON_INTERPRETER - Target or path to the Python interpreter to use to run generation scripts # MSGS_GEN_SCRIPT - Path to the message generation python script # PROTO_PACKAGE - Protobuf package the file belongs to (e.g. "gz.msgs") # PROTOC_EXEC - Path to protoc @@ -22,6 +23,7 @@ function(gz_msgs_protoc) set(options GENERATE_CPP GENERATE_PYTHON) set(oneValueArgs + PYTHON_INTERPRETER MSGS_GEN_SCRIPT PROTO_PACKAGE PROTOC_EXEC @@ -38,6 +40,13 @@ function(gz_msgs_protoc) cmake_parse_arguments(gz_msgs_protoc "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + if (NOT DEFINED gz_msgs_protoc_PYTHON_INTERPRETER) + set(gz_msgs_protoc_PYTHON_INTERPRETER Python3::Interpreter) + endif() + if (NOT DEFINED gz_msgs_protoc_PROTOC_EXEC) + set(gz_msgs_protoc_PROTOC_EXEC protobuf::protoc) + endif() + get_filename_component(ABS_FIL ${gz_msgs_protoc_INPUT_PROTO} ABSOLUTE) get_filename_component(FIL_WE ${gz_msgs_protoc_INPUT_PROTO} NAME_WE) @@ -85,8 +94,14 @@ function(gz_msgs_protoc) set(${gz_msgs_protoc_OUTPUT_PYTHON_VAR} ${${gz_msgs_protoc_OUTPUT_PYTHON_VAR}} PARENT_SCOPE) endif() + if(TARGET ${gz_msgs_protoc_PROTOC_EXEC}) + set(gz_msgs_protoc_PROTOC_EXEC_FILE_ABS_PATH "$") + else() + set(gz_msgs_protoc_PROTOC_EXEC_FILE_ABS_PATH ${gz_msgs_protoc_PROTOC_EXEC}) + endif() + set(GENERATE_ARGS - --protoc-exec "$" + --protoc-exec "${gz_msgs_protoc_PROTOC_EXEC_FILE_ABS_PATH}" --gz-generator-bin "${gz_msgs_protoc_GZ_PROTOC_PLUGIN}" --proto-path "${gz_msgs_protoc_PROTO_PATH}" --input-path "${ABS_FIL}" @@ -118,7 +133,7 @@ function(gz_msgs_protoc) add_custom_command( OUTPUT ${output_files} - COMMAND Python3::Interpreter + COMMAND ${gz_msgs_protoc_PYTHON_INTERPRETER} ARGS ${gz_msgs_protoc_MSGS_GEN_SCRIPT} ${GENERATE_ARGS} DEPENDS ${ABS_FIL} diff --git a/conf/CMakeLists.txt b/conf/CMakeLists.txt index bb60e426..5bae2b3c 100644 --- a/conf/CMakeLists.txt +++ b/conf/CMakeLists.txt @@ -1,12 +1,16 @@ # Used only for internal testing. -set(gz_library_path "${CMAKE_BINARY_DIR}/test/lib/ruby/gz/cmd${GZ_DESIGNATION}${PROJECT_VERSION_MAJOR}") +set(gz_library_path "${CMAKE_BINARY_DIR}/test/lib/$/ruby/gz/cmd${GZ_DESIGNATION}${PROJECT_VERSION_MAJOR}") # Generate a configuration file for internal testing. # Note that the major version of the library is included in the name. # Ex: transport0.yaml configure_file( "${GZ_DESIGNATION}.yaml.in" - "${CMAKE_BINARY_DIR}/test/conf/${GZ_DESIGNATION}${PROJECT_VERSION_MAJOR}.yaml" @ONLY) + "${CMAKE_CURRENT_BINARY_DIR}/${GZ_DESIGNATION}${PROJECT_VERSION_MAJOR}.yaml.configured" @ONLY) + +file(GENERATE + OUTPUT "${CMAKE_BINARY_DIR}/test/conf/$/${GZ_DESIGNATION}${PROJECT_VERSION_MAJOR}.yaml" + INPUT "${CMAKE_CURRENT_BINARY_DIR}/${GZ_DESIGNATION}${PROJECT_VERSION_MAJOR}.yaml.configured") # Used for the installed version. set(gz_library_path "${CMAKE_INSTALL_PREFIX}/lib/ruby/gz/cmd${GZ_DESIGNATION}${PROJECT_VERSION_MAJOR}") diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 75ae6e56..951b72d5 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -26,13 +26,23 @@ include(${PROJECT_SOURCE_DIR}/cmake/gz_msgs_generate.cmake) include(${PROJECT_SOURCE_DIR}/cmake/gz_msgs_protoc.cmake) include(${PROJECT_SOURCE_DIR}/cmake/gz_msgs_string_utils.cmake) +if(TARGET ${${PROJECT_NAME}_PROTO_GENERATOR_PLUGIN}) + set(${PROJECT_NAME}_PROTO_GENERATOR_PLUGIN_FILE $) +else() + set(${PROJECT_NAME}_PROTO_GENERATOR_PLUGIN_FILE ${${PROJECT_NAME}_PROTO_GENERATOR_PLUGIN}) +endif() + gz_msgs_generate_messages_impl( MSGS_GEN_SCRIPT ${PROJECT_SOURCE_DIR}/tools/gz_msgs_generate.py FACTORY_GEN_SCRIPT ${PROJECT_SOURCE_DIR}/tools/gz_msgs_generate_factory.py + PYTHON_INTERPRETER + ${${PROJECT_NAME}_PYTHON_INTERPRETER} + PROTOC_EXEC + ${${PROJECT_NAME}_PROTOC_EXECUTABLE} GZ_PROTOC_PLUGIN - $ + ${${PROJECT_NAME}_PROTO_GENERATOR_PLUGIN_FILE} INPUT_PROTOS ${proto_files} DLLEXPORT_DECL @@ -54,6 +64,7 @@ gz_msgs_generate_messages_impl( ) gz_msgs_generate_desc_impl( + PROTOC_EXEC ${${PROJECT_NAME}_PROTOC_EXECUTABLE} INPUT_PROTOS ${proto_files} PROTO_PATH ${PROJECT_SOURCE_DIR}/proto DEPENDENCY_DESCRIPTIONS ${depends_msgs_desc} diff --git a/core/generator/Generator.cc b/core/generator/Generator.cc index 1fafd43b..9c835242 100644 --- a/core/generator/Generator.cc +++ b/core/generator/Generator.cc @@ -43,21 +43,7 @@ #include "Generator.hh" -namespace google { -namespace protobuf { -namespace compiler { -namespace cpp { - -///////////////////////////////////////////////// -void replaceAll(std::string &_src, const std::string &_oldValue, - const std::string &_newValue) -{ - for (size_t i = 0; (i = _src.find(_oldValue, i)) != std::string::npos;) - { - _src.replace(i, _oldValue.length(), _newValue); - i += _newValue.length() - _oldValue.length() + 1; - } -} +namespace google::protobuf::compiler::cpp { ///////////////////////////////////////////////// std::vector getNamespaces(const std::string &_package) @@ -65,13 +51,12 @@ std::vector getNamespaces(const std::string &_package) std::vector result; std::stringstream ss(_package); std::string item; - while (getline (ss, item, '.')) { - result.push_back (item); + while(getline (ss, item, '.')) { + result.push_back(item); } return result; } - ///////////////////////////////////////////////// Generator::Generator(const std::string &/*_name*/) { @@ -102,26 +87,23 @@ bool Generator::Generate(const FileDescriptor *_file, std::string identifier; std::string headerFilename; std::string newHeaderFilename; - std::string sourceFilename; // Can't use pathJoin because protoc always expects forward slashes // regardless of platform - for (auto part : parent_path) + for (const auto &part : parent_path) { identifier += part.string() + "_"; headerFilename += part.string() + "/"; newHeaderFilename += part.string() + "/"; - sourceFilename += part.string() + "/"; } - auto message_type_index = - _generatorContext->Open(identifier + fileStem + ".pb_index"); - io::Printer indexPrinter(message_type_index, '$'); + std::unique_ptr message_type_index( + _generatorContext->Open(identifier + fileStem + ".pb_index")); + io::Printer indexPrinter(message_type_index.get(), '$'); identifier += fileStem; headerFilename += fileStem + ".gz.h"; newHeaderFilename += "details/" + fileStem + ".pb.h"; - sourceFilename += fileStem + ".pb.cc"; std::map variables; variables["filename"] = _file->name(); @@ -154,7 +136,7 @@ bool Generator::Generate(const FileDescriptor *_file, for (auto i = 0; i < _file->message_type_count(); ++i) { - auto desc = _file->message_type(i); + const auto *desc = _file->message_type(i); std::string ptrTypes; indexPrinter.PrintRaw(desc->name()); @@ -195,7 +177,4 @@ bool Generator::Generate(const FileDescriptor *_file, return true; } -} -} -} -} +} // namespace google::protobuf::compiler::cpp diff --git a/core/include/gz/msgs/convert/Pose.hh b/core/include/gz/msgs/convert/Pose.hh index 3f7c1c22..f2efea7c 100644 --- a/core/include/gz/msgs/convert/Pose.hh +++ b/core/include/gz/msgs/convert/Pose.hh @@ -22,7 +22,7 @@ #include // Message Headers -#include "gz/msgs/details/quaternion.pb.h" +#include "gz/msgs/quaternion.pb.h" #include "gz/msgs/pose.pb.h" // Data Headers diff --git a/core/src/DynamicFactory.cc b/core/src/DynamicFactory.cc index a8a7dac3..2ef60f3f 100644 --- a/core/src/DynamicFactory.cc +++ b/core/src/DynamicFactory.cc @@ -65,11 +65,15 @@ DynamicFactory::DynamicFactory() // Load all the descriptors found in the paths set with GZ_DESCRIPTOR_PATH. this->LoadDescriptors(descPaths); } - else + + auto globalPath = + std::filesystem::path(gz::msgs::getInstallPrefix()) / + "share" / "gz" / "protos"; + + if (std::filesystem::exists(globalPath)) { // Load descriptors from the global share path - this->LoadDescriptors((std::filesystem::path( - gz::msgs::getInstallPrefix()) / "share" / "gz" / "protos").string()); + this->LoadDescriptors(globalPath.string()); } } @@ -83,58 +87,60 @@ void DynamicFactory::LoadDescriptors(const std::string &_paths) std::vector descDirs = split(_paths, kEnvironmentVariableSeparator); - - for (const std::string &descDir : descDirs) + auto loadDescFile = [this](const std::string &descFile) { - for (auto const &dirIter : std::filesystem::directory_iterator{descDir}) + // Ignore files without the .desc extension. + if (descFile.rfind(".desc") == std::string::npos && + descFile.rfind(".gz_desc") == std::string::npos && + descFile.rfind(".proto") == std::string::npos && + descFile.rfind(".proto.bin") == std::string::npos) + return; + + // Parse the .desc file. + std::ifstream ifs(descFile); + if (!ifs.is_open()) { - // Ignore files without the .desc extension. - if (dirIter.path().extension() != ".desc" && - dirIter.path().extension() != ".gz_desc") - continue; - - std::ifstream ifs(dirIter.path().string(), std::ifstream::in); - if (!ifs.is_open()) + std::cerr << "DynamicFactory(): Unable to open [" << descFile << "]" + << std::endl; + return; + } + google::protobuf::FileDescriptorSet fileDescriptorSet; + if (!fileDescriptorSet.ParseFromIstream(&ifs)) + { + std::cerr << "DynamicFactory(): Unable to parse descriptor set from [" + << descFile << "]" << std::endl; + return; + } + // Place the real descriptors in the descriptor pool. + for (const google::protobuf::FileDescriptorProto &fileDescriptorProto : + fileDescriptorSet.file()) + { + if (!this->pool.BuildFile(fileDescriptorProto)) { - std::cerr << "DynamicFactory(): Unable to open [" - << dirIter.path() << "]" - << std::endl; - continue; + std::cerr << "DynamicFactory(). Unable to place descriptors from [" + << descFile << "] in the descriptor pool" << std::endl; } - - google::protobuf::FileDescriptorSet fileDescriptorSet; - if (!fileDescriptorSet.ParseFromIstream(&ifs)) + else { - std::cerr << "DynamicFactory(): Unable to parse descriptor set from [" - << dirIter.path() << "]" << std::endl; - continue; + this->db.Add(fileDescriptorProto); } + } + }; - for (const google::protobuf::FileDescriptorProto &fileDescriptorProto : - fileDescriptorSet.file()) + for (const std::string &descDir : descDirs) + { + if (!std::filesystem::is_directory(descDir)) + { + loadDescFile(descDir); + } + else + { + for (auto const &dirIter : std::filesystem::directory_iterator{descDir}) { - // If the descriptor already exists in the database, then skip it. - // This may happen as gz_desc files can potentially contain the - // transitive message definitions - google::protobuf::FileDescriptorProto checkDescriptorProto; - if (this->db.FindFileByName( - fileDescriptorProto.name(), &checkDescriptorProto)) - { - continue; - } - - if (!static_cast(pool.BuildFile(fileDescriptorProto))) - { - std::cerr << "DynamicFactory(). Unable to place descriptors from [" - << dirIter.path() - << "] in the descriptor pool" << std::endl; - } - - this->db.Add(fileDescriptorProto); + loadDescFile(dirIter.path().string()); } } } - } ////////////////////////////////////////////////// diff --git a/examples/generating_custom_msgs/CMakeLists.txt b/examples/generating_custom_msgs/CMakeLists.txt index 1ad64b48..eb34f1c1 100644 --- a/examples/generating_custom_msgs/CMakeLists.txt +++ b/examples/generating_custom_msgs/CMakeLists.txt @@ -11,15 +11,17 @@ project(generating_custom_messages VERSION 1.0.0) find_package(gz-cmake4 REQUIRED) find_package(gz-msgs11 REQUIRED) +# Define a variable 'GZ_MSGS_VER' holding the version number set(GZ_MSGS_VER ${gz-msgs11_VERSION_MAJOR}) -# Example of custom messages that depend on gz.msgs +# Define a variable 'MSGS_PROTOS' listing the .proto files set(MSGS_PROTOS ${CMAKE_CURRENT_SOURCE_DIR}/proto/gz/custom_msgs/foo.proto ${CMAKE_CURRENT_SOURCE_DIR}/proto/gz/custom_msgs/bar.proto ${CMAKE_CURRENT_SOURCE_DIR}/proto/gz/custom_msgs/baz.proto ) +# Call 'gz_msgs_generate_messages()' to process the .proto files gz_msgs_generate_messages( # The cmake target to be generated for libraries/executables to link TARGET msgs @@ -28,8 +30,9 @@ gz_msgs_generate_messages( # The path to the base directory of the proto files # All import paths should be relative to this (eg gz/custom_msgs/vector3d.proto) MSGS_PATH ${CMAKE_CURRENT_SOURCE_DIR}/proto - # List of proto files to generate + # List of proto files to process MSGS_PROTOS ${MSGS_PROTOS} + # Depenency on gz-msgs DEPENDENCIES gz-msgs${GZ_MSGS_VER}::gz-msgs${GZ_MSGS_VER} ) diff --git a/gz-msgs-extras.cmake.in b/gz-msgs-extras.cmake.in index 914f779a..8bbdbe5a 100644 --- a/gz-msgs-extras.cmake.in +++ b/gz-msgs-extras.cmake.in @@ -22,13 +22,30 @@ include(${@PROJECT_NAME@_DIR}/gz_msgs_factory.cmake) include(${@PROJECT_NAME@_DIR}/gz_msgs_generate.cmake) set(@PROJECT_NAME@_INSTALL_PATH "${@PROJECT_NAME@_DIR}/@PROJECT_CMAKE_EXTRAS_PATH_TO_PREFIX@") -cmake_path(NORMAL_PATH @PROJECT_NAME@_INSTALL_PATH OUTPUT_VARIABLE @PROJECT_NAME@_INSTALL_PATH) + +if(CMAKE_VERSION VERSION_LESS "3.20.0") + file(TO_CMAKE_PATH @PROJECT_NAME@_INSTALL_PATH NORMALIZED_PATH) # Converts native path to CMake style with forward slashes. + get_filename_component(ABSOLUTE_PATH "${NORMALIZED_PATH}" ABSOLUTE) # Extracts the absolute path component. + set(@PROJECT_NAME@_INSTALL_PATH "${ABSOLUTE_PATH}") # Stores the normalized absolute path back to the original variable. +else() + cmake_path(NORMAL_PATH @PROJECT_NAME@_INSTALL_PATH OUTPUT_VARIABLE @PROJECT_NAME@_INSTALL_PATH) +endif() + set(PROTOC_NAME "$") set(PROTO_SCRIPT_NAME "@PROJECT_NAME@_generate.py") set(FACTORY_SCRIPT_NAME "@PROJECT_NAME@_generate_factory.py") set(@PROJECT_NAME@_PROTO_PATH ${@PROJECT_NAME@_INSTALL_PATH}/share/protos) -set(@PROJECT_NAME@_PROTO_GENERATOR_PLUGIN ${@PROJECT_NAME@_INSTALL_PATH}/bin/${PROTOC_NAME}) +# Provide support to override generator executable used during cross-compilation +if(NOT DEFINED @PROJECT_NAME@_PROTO_GENERATOR_PLUGIN) + set(@PROJECT_NAME@_PROTO_GENERATOR_PLUGIN ${@PROJECT_NAME@_INSTALL_PATH}/bin/${PROTOC_NAME}) +endif() +if(NOT DEFINED @PROJECT_NAME@_PROTOC_EXECUTABLE) + set(@PROJECT_NAME@_PROTOC_EXECUTABLE protobuf::protoc) +endif() +if(NOT DEFINED @PROJECT_NAME@_PYTHON_INTERPRETER) + set(@PROJECT_NAME@_PYTHON_INTERPRETER Python3::Interpreter) +endif() set(@PROJECT_NAME@_PROTO_GENERATOR_SCRIPT ${@PROJECT_NAME@_INSTALL_PATH}/bin/${PROTO_SCRIPT_NAME}) set(@PROJECT_NAME@_FACTORY_GENERATOR_SCRIPT ${@PROJECT_NAME@_INSTALL_PATH}/bin/${FACTORY_SCRIPT_NAME}) @@ -64,6 +81,10 @@ function(gz_msgs_generate_messages) cmake_parse_arguments(generate_messages "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) gz_msgs_generate_messages_lib( + PYTHON_INTERPRETER + ${@PROJECT_NAME@_PYTHON_INTERPRETER} + PROTOC_EXEC + ${@PROJECT_NAME@_PROTOC_EXECUTABLE} MSGS_GEN_SCRIPT ${@PROJECT_NAME@_PROTO_GENERATOR_SCRIPT} FACTORY_GEN_SCRIPT diff --git a/gz_msg_gen.bzl b/gz_msg_gen.bzl deleted file mode 100644 index 477e4875..00000000 --- a/gz_msg_gen.bzl +++ /dev/null @@ -1,76 +0,0 @@ -load("@rules_proto//proto:defs.bzl", "ProtoInfo") -load( - ":protobuf.bzl", - "declare_out_files", - "get_include_directory", - "get_out_dir", - "proto_path_to_generated_filename", - "protos_from_context", -) - -def gz_msg_gen_impl(ctx): - protos = protos_from_context(ctx) - dir_out = get_out_dir(protos, ctx) - - cc_files = declare_out_files(protos, ctx, "{}.pb.cc") - hh_files = declare_out_files(protos, ctx, "{}.pb.h") - out_files = cc_files + hh_files - - include_dirs = depset([get_include_directory(proto) for proto in protos]) - - args = [ - "--cpp_out=" + dir_out.path, - "--plugin=protoc-gen-gzmsgs=" + ctx.executable._plugin.path, - "--gzmsgs_out=" + dir_out.path, - ] - - for include_dir in include_dirs.to_list(): - args.append("--proto_path=" + include_dir) - - ctx.actions.run( - outputs = out_files, - inputs = protos, - tools = [ctx.executable._plugin, ctx.executable._protoc], - executable = ctx.executable._protoc, - arguments = args + [proto.path for proto in protos], - ) - - compilation_context = cc_common.create_compilation_context( - includes = depset([dir_out.path]), - ) - - return [ - DefaultInfo(files = depset(out_files)), - CcInfo(compilation_context = compilation_context), - ] - -gz_msg_gen = rule( - attrs = { - "deps": attr.label_list( - mandatory = True, - allow_empty = False, - providers = [ProtoInfo], - ), - "_plugin": attr.label( - default = Label("//gz_msgs:gz_msgs_gen"), - executable = True, - cfg = "host", - ), - "_protoc": attr.label( - default = Label("@com_google_protobuf//:protoc"), - executable = True, - cfg = "host", - ), - }, - # We generate .h files, so we need to output to genfiles. - output_to_genfiles = True, - implementation = gz_msg_gen_impl, -) - -def get_proto_headers(protos): - out = [] - for proto in protos: - split = proto.split("/")[1:] - split[2] = split[2].replace(".proto", ".pb.h") - out.append("/".join(split)) - return out diff --git a/proto/gz/msgs/material_color.proto b/proto/gz/msgs/material_color.proto new file mode 100644 index 00000000..a8b4f44f --- /dev/null +++ b/proto/gz/msgs/material_color.proto @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2024 CogniPilot Foundation + * Copyright (C) 2024 Rudis Laboratories LLC + * + * 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. + * +*/ + +syntax = "proto3"; +package gz.msgs; +option java_package = "com.gz.msgs"; +option java_outer_classname = "MaterialColorProtos"; + +/// \ingroup gz.msgs +/// \interface MaterialColor +/// \brief Color description for an entities link-visual material. + +import "gz/msgs/color.proto"; +import "gz/msgs/entity.proto"; +import "gz/msgs/header.proto"; + +message MaterialColor +{ + /// \brief Entities that match to apply material color. + enum EntityMatch + { + /// \brief Apply material color to first matching entity. + FIRST = 0; + + /// \brief Apply material color to all matching entities. + ALL = 1; + } + + /// \brief Optional header data + Header header = 1; + + /// \brief Entity for which material colors are going to be modified. + /// + /// The entity should be a visual element of a link that may exist + /// in a world or model. + /// The entity name (entity.name) will be used as an scoped name. + /// For example, in this + /// hierarchy: + /// + /// world_name + /// model_name + /// link_name + /// visual_name + /// + /// All these names will return the link entity: + /// + /// * world_name::model_name::link_name::visual_name + /// * model_name::link_name::visual_name + /// * link_name::visual_name + /// * visual_name + Entity entity = 2; + + /// \brief Ambient color + Color ambient = 3; + + /// \brief Diffuse color + Color diffuse = 4; + + /// \brief Specular color + Color specular = 5; + + /// \brief Emissive color + Color emissive = 6; + + /// \brief Specular exponent + double shininess = 7; + + /// \brief Entities that match to apply material color. + EntityMatch entity_match = 8; +} diff --git a/test/desc/testing.invalid b/test/desc/testing.invalid new file mode 100644 index 00000000..b2524164 --- /dev/null +++ b/test/desc/testing.invalid @@ -0,0 +1,8 @@ +syntax = "proto3"; + +package testing; + +message Bytes +{ + bytes data = 1; +} diff --git a/test/desc/testing.proto b/test/desc/testing.proto new file mode 100644 index 00000000..b2524164 --- /dev/null +++ b/test/desc/testing.proto @@ -0,0 +1,8 @@ +syntax = "proto3"; + +package testing; + +message Bytes +{ + bytes data = 1; +} diff --git a/test/desc/testing.proto.bin b/test/desc/testing.proto.bin new file mode 100644 index 00000000..ea7cbe8c --- /dev/null +++ b/test/desc/testing.proto.bin @@ -0,0 +1,16 @@ + +C +testing/bytes.prototesting" +Bytes +data ( Rdatabproto3 +ˆ +testing/message.prototesting" + +FooMessage +foo ( Rfoo" + +BarMessage +foo ( Rfoo" + +BazMessage +foo ( Rfoobproto3 \ No newline at end of file diff --git a/test/desc/testing/bytes.proto b/test/desc/testing/bytes.proto new file mode 100644 index 00000000..b2524164 --- /dev/null +++ b/test/desc/testing/bytes.proto @@ -0,0 +1,8 @@ +syntax = "proto3"; + +package testing; + +message Bytes +{ + bytes data = 1; +} diff --git a/test/desc/testing/message.proto b/test/desc/testing/message.proto new file mode 100644 index 00000000..751197d6 --- /dev/null +++ b/test/desc/testing/message.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; + +package testing; + +message FooMessage { + string data = 1; +} + +message BarMessage { + FooMessage foo = 1; +} + +message BazMessage { + BarMessage bar = 1; +} diff --git a/test/integration/CMakeLists.txt b/test/integration/CMakeLists.txt index b0baa6b3..63230d90 100644 --- a/test/integration/CMakeLists.txt +++ b/test/integration/CMakeLists.txt @@ -4,7 +4,8 @@ if (NOT HAVE_GZ_TOOLS) list(REMOVE_ITEM tests gz_TEST.cc) endif() -gz_build_tests(TYPE INTEGRATION SOURCES ${tests} TEST_LIST test_targets) +gz_build_tests(TYPE INTEGRATION SOURCES ${tests} TEST_LIST test_targets + ENVIRONMENT GZ_MSG_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}) if(TARGET INTEGRATION_gz_TEST) target_compile_definitions(INTEGRATION_gz_TEST PRIVATE @@ -12,7 +13,17 @@ if(TARGET INTEGRATION_gz_TEST) "GZ_MSGS_COMPLETION_SCRIPT_PATH=\"${PROJECT_SOURCE_DIR}/core/cmd/msgs.bash_completion.sh\"") endif() +if(TARGET INTEGRATION_descriptors) + target_compile_definitions(INTEGRATION_descriptors PRIVATE + "-DGZ_MSGS_TEST_PATH=\"${PROJECT_SOURCE_DIR}/test\"") +endif() + if(TARGET INTEGRATION_Factory_TEST) target_compile_definitions(INTEGRATION_Factory_TEST PRIVATE "GZ_MSGS_TEST_PATH=\"${PROJECT_SOURCE_DIR}/test\"") endif() + +if(TARGET INTEGRATION_gz_TEST) + target_compile_definitions(INTEGRATION_gz_TEST PUBLIC + "-DDETAIL_GZ_CONFIG_PATH=\"${CMAKE_BINARY_DIR}/test/conf/$\"") +endif() diff --git a/test/integration/descriptors.cc b/test/integration/descriptors.cc new file mode 100644 index 00000000..2d7ee0d5 --- /dev/null +++ b/test/integration/descriptors.cc @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2024 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 "gz/msgs/Factory.hh" + +static constexpr const char * kMsgsTestPath = GZ_MSGS_TEST_PATH; + +TEST(FactoryTest, DynamicFactory) +{ + EXPECT_EQ(nullptr, gz::msgs::Factory::New("example.msgs.StringMsg")); + EXPECT_EQ(nullptr, gz::msgs::Factory::New("testing.Bytes")); + EXPECT_EQ(nullptr, gz::msgs::Factory::New("testing.FooMessage")); + EXPECT_EQ(nullptr, gz::msgs::Factory::New("testing.BarMessage")); + EXPECT_EQ(nullptr, gz::msgs::Factory::New("testing.BazMessage")); + + // Test loading an invalid path + { + std::filesystem::path test_path(kMsgsTestPath); + std::string paths = (test_path / "desc" / "does_not_exist.desc").string(); + gz::msgs::Factory::LoadDescriptors(paths); + } + EXPECT_EQ(nullptr, gz::msgs::Factory::New("example.msgs.StringMsg")); + EXPECT_EQ(nullptr, gz::msgs::Factory::New("testing.Bytes")); + EXPECT_EQ(nullptr, gz::msgs::Factory::New("testing.FooMessage")); + EXPECT_EQ(nullptr, gz::msgs::Factory::New("testing.BarMessage")); + EXPECT_EQ(nullptr, gz::msgs::Factory::New("testing.BazMessage")); + + // Test loading an incorrect extension + { + std::filesystem::path test_path(kMsgsTestPath); + std::string paths = (test_path / "desc" / "testing.invalid").string(); + gz::msgs::Factory::LoadDescriptors(paths); + } + EXPECT_EQ(nullptr, gz::msgs::Factory::New("example.msgs.StringMsg")); + EXPECT_EQ(nullptr, gz::msgs::Factory::New("testing.Bytes")); + EXPECT_EQ(nullptr, gz::msgs::Factory::New("testing.FooMessage")); + EXPECT_EQ(nullptr, gz::msgs::Factory::New("testing.BarMessage")); + EXPECT_EQ(nullptr, gz::msgs::Factory::New("testing.BazMessage")); + + // Test loading a file with invalid content + { + std::filesystem::path test_path(kMsgsTestPath); + std::string paths = (test_path / "desc" / "testing.proto").string(); + gz::msgs::Factory::LoadDescriptors(paths); + } + EXPECT_EQ(nullptr, gz::msgs::Factory::New("example.msgs.StringMsg")); + EXPECT_EQ(nullptr, gz::msgs::Factory::New("testing.Bytes")); + EXPECT_EQ(nullptr, gz::msgs::Factory::New("testing.FooMessage")); + EXPECT_EQ(nullptr, gz::msgs::Factory::New("testing.BarMessage")); + EXPECT_EQ(nullptr, gz::msgs::Factory::New("testing.BazMessage")); + + // Load a valid descriptor file with one message type + { + std::filesystem::path test_path(kMsgsTestPath); + std::string paths = (test_path / "desc" / "stringmsg.desc").string(); + gz::msgs::Factory::LoadDescriptors(paths); + } + + EXPECT_NE(nullptr, gz::msgs::Factory::New("example.msgs.StringMsg")); + EXPECT_EQ(nullptr, gz::msgs::Factory::New("testing.Bytes")); + EXPECT_EQ(nullptr, gz::msgs::Factory::New("testing.FooMessage")); + EXPECT_EQ(nullptr, gz::msgs::Factory::New("testing.BarMessage")); + EXPECT_EQ(nullptr, gz::msgs::Factory::New("testing.BazMessage")); + + // Load a directory + { + std::filesystem::path test_path(kMsgsTestPath); + std::string paths = (test_path / "desc").string(); + gz::msgs::Factory::LoadDescriptors(paths); + } + + EXPECT_NE(nullptr, gz::msgs::Factory::New("example.msgs.StringMsg")); + EXPECT_NE(nullptr, gz::msgs::Factory::New("testing.Bytes")); + EXPECT_NE(nullptr, gz::msgs::Factory::New("testing.FooMessage")); + EXPECT_NE(nullptr, gz::msgs::Factory::New("testing.BarMessage")); + EXPECT_NE(nullptr, gz::msgs::Factory::New("testing.BazMessage")); +} diff --git a/test/integration/headers.cc b/test/integration/headers.cc index fe988858..64b88e49 100644 --- a/test/integration/headers.cc +++ b/test/integration/headers.cc @@ -16,7 +16,6 @@ */ #include -#include #include "gz/msgs/contact.pb.h" ///////////////////////////////////////////////// diff --git a/test/performance/CMakeLists.txt b/test/performance/CMakeLists.txt index 0a11145c..a6a1be44 100644 --- a/test/performance/CMakeLists.txt +++ b/test/performance/CMakeLists.txt @@ -1,3 +1,4 @@ gz_get_sources(tests) -gz_build_tests(TYPE PERFORMANCE SOURCES ${tests}) +gz_build_tests(TYPE PERFORMANCE SOURCES ${tests} + ENVIRONMENT GZ_MSG_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}) diff --git a/test/regression/CMakeLists.txt b/test/regression/CMakeLists.txt index 762a0520..134ea124 100644 --- a/test/regression/CMakeLists.txt +++ b/test/regression/CMakeLists.txt @@ -1,3 +1,4 @@ gz_get_sources(tests) -gz_build_tests(TYPE REGRESSION SOURCES ${tests}) +gz_build_tests(TYPE REGRESSION SOURCES ${tests} + ENVIRONMENT GZ_MSG_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}) diff --git a/test/test_config.hh.in b/test/test_config.hh.in index 073b407a..429d6cc8 100644 --- a/test/test_config.hh.in +++ b/test/test_config.hh.in @@ -19,7 +19,6 @@ #define GZ_MSGS_TEST_CONFIG_HH_ #define PROJECT_SOURCE_PATH "${PROJECT_SOURCE_DIR}" -#define GZ_CONFIG_PATH "@CMAKE_BINARY_DIR@/test/conf" #define GZ_TEST_LIBRARY_PATH "${PROJECT_BINARY_DIR}/src" #if (_MSC_VER >= 1400) // Visual Studio 2005 diff --git a/tools/BUILD.bazel b/tools/BUILD.bazel index 8bdd14a8..bba0c8c1 100644 --- a/tools/BUILD.bazel +++ b/tools/BUILD.bazel @@ -1,7 +1,8 @@ -load("@gz//bazel/skylark:gz_py.bzl", "gz_py_binary") +load("@gz//bazel/skylark:build_defs.bzl", "gz_py_binary") gz_py_binary( name = "gz_msgs_generate_py", srcs = ["gz_msgs_generate.py"], + main = "gz_msgs_generate.py", visibility = ["//visibility:public"], ) diff --git a/tutorials/message_generation.md b/tutorials/message_generation.md index 43416223..ffab1054 100644 --- a/tutorials/message_generation.md +++ b/tutorials/message_generation.md @@ -6,7 +6,7 @@ This tutorial describes how Gazebo messages are generated and you can create custom messages that can be used with the simulator and command line tools. Gazebo messages use [Protobuf](https://protobuf.dev) to define the structures -that can be easily serialized for use in communication through the Gazebo +that can be easily serialized for use in communication through the Gazebo software stack. Protobuf is a language-neutral framework for serializing structured data, with the advantage of generating native language bindings so that messages are easily used from your language of choice. @@ -15,7 +15,7 @@ so that messages are easily used from your language of choice. ### File structure -Gazebo message definitions are stored in the +Gazebo message definitions are stored in the [proto](https://github.com/gazebosim/gz-msgs/tree/main/proto) folder. Messages may additionally belong to a `package`, which allows for convenient @@ -49,7 +49,7 @@ import "gz/msgs/vector3d.proto"; message FooMessage { /// Message header data (timestamp) - /// Since this definition is also in gz.msgs, the fully-qualified + /// Since this definition is also in gz.msgs, the fully-qualified /// name isn't necessary here, but left in for explicitness. gz.msgs.Header header = 1; @@ -66,7 +66,7 @@ Each field must be given an ID between `1` and `536,870,911`, with the following restrctions (from the [language guide](https://protobuf.dev/programming-guides/proto3/#assigning)): * The given number must be unique among all fields for that message. -* Field numbers `19,000` to `19,999` are reserved for the Protocol Buffers implementation. +* Field numbers `19,000` to `19,999` are reserved for the Protocol Buffers implementation. The protocol buffer compiler will complain if you use one of these reserved field numbers in your message. * You cannot use any previously reserved field numbers or any field numbers that have been allocated to extensions. @@ -77,7 +77,7 @@ restrctions (from the [language guide](https://protobuf.dev/programming-guides/p Once the proto messages have been defined, there are several steps that take place to make the messages usable across the Gazebo stack. -To begin with, each message definition is passed through the `gz_msgs_protoc` +To begin with, each message definition is passed through the `gz_msgs_protoc` function: \image html files/gz_msgs_protoc.svg @@ -106,7 +106,7 @@ are grouped and processed via `gz_msgs_factory` For the collection, `gz_msgs_factory` generates: -* A `register.cc` file that can be used to statically register all of the +* A `register.cc` file that can be used to statically register all of the messages passed as arguments. * A `MessageTypes.hh` file that enumerates all messages available in the collection. @@ -116,9 +116,13 @@ For the collection, `gz_msgs_factory` generates: ## Custom Message Generation -Now that we understand the components of the message generation pipeline, +Now that we understand the components of the message generation pipeline, we can use them in our own custom package. +The code for this example can be found in the `gz-msgs` [repository](https://github.com/gazebosim/gz-msgs/tree/main), in the [`examples/generating_custom_msgs`](https://github.com/gazebosim/gz-msgs/tree/main/examples/generating_custom_msgs) folder. + + + The `cmake` functionality is exported from the `gz-msgs` library, via the `gz-cmake` [`extras` functionality](https://github.com/gazebosim/gz-cmake/pull/345). To make the functions available, simply `find_package(gz-msgs11)` in your `CMakeLists.txt`: @@ -129,6 +133,7 @@ find_package(gz-cmake4 REQUIRED) find_package(gz-msgs11 REQUIRED) ``` + Next, create a directory for your custom message definitions: ```sh @@ -158,7 +163,7 @@ message Foo syntax = "proto3"; package gz.custom_msgs; -message Bar +message Bar { double value = 1; } @@ -189,9 +194,17 @@ message BazStamped ``` -Then, back in the `CMakeLists.txt` file, generate the message library. +Then, back in the `CMakeLists.txt` file, add following lines to generate the message library: ```cmake +# Define a variable 'MSGS_PROTOS' listing the .proto files +set(MSGS_PROTOS + ${CMAKE_CURRENT_SOURCE_DIR}/proto/gz/custom_msgs/foo.proto + ${CMAKE_CURRENT_SOURCE_DIR}/proto/gz/custom_msgs/bar.proto + ${CMAKE_CURRENT_SOURCE_DIR}/proto/gz/custom_msgs/baz.proto +) + +# Call 'gz_msgs_generate_messages()' to process the .proto files gz_msgs_generate_messages( # The cmake target to be generated for libraries/executables to link TARGET msgs @@ -200,12 +213,25 @@ gz_msgs_generate_messages( # The path to the base directory of the proto files # All import paths should be relative to this (eg gz/custom_msgs/vector3d.proto) MSGS_PATH ${CMAKE_CURRENT_SOURCE_DIR}/proto - # List of proto files to generate + # List of proto files to process MSGS_PROTOS ${MSGS_PROTOS} - DEPENDENCIES gz-msgs${GZ_MSGS_VER}::gz-msgs${GZ_MSGS_VER} + # Depenency on gz-msgs + DEPENDENCIES gz-msgs11::gz-msgs11 ) ``` +In order to reduce the amount of edits needed upon a version change of `gz-msgs`, it is common to: + + - Define a variable `GZ_MSGS_VER`, holding the version number: + ```cmake + find_package(gz-msgs11 REQUIRED) + set(GZ_MSGS_VER ${gz-msgs11_VERSION_MAJOR}) + ``` + - And change the dependency line in above code block to: + ```cmake + DEPENDENCIES gz-msgs${GZ_MSGS_VER}::gz-msgs${GZ_MSGS_VER} + ``` + ### Using Custom messages There are two primary ways that Gazebo messages are used as part of an application. @@ -246,7 +272,7 @@ target_link_libraries(${PROJECT_NAME} PUBLIC ${PROJECT_NAME}-msgs) Alternatively, there may be cases where all message definitions are not known at the time that the executable/library of interest is being built. A common case for this is with command line tools, where only a limited -set of messages are available at the time the tool is built, and more +set of messages are available at the time the tool is built, and more user-defined messages are generated later. For example, when custom messages are generated, they aren't initially visible @@ -260,7 +286,7 @@ $ gz topic -t /foo -m gz.custom_msgs.Foo -p 'value: 1.0' Unable to create message of type[gz.custom_msgs.Foo] with data[value: 1.0]. ``` -To use the new messages, point the `GZ_DESCRIPTOR_PATH` environment variable to +To use the new messages, point the `GZ_DESCRIPTOR_PATH` environment variable to the location of the `build` folder or where you have choosen to install the `.gz_desc` file: @@ -270,9 +296,9 @@ $ cd build/ $ export GZ_DESCRIPTOR_PATH=`pwd` $ gz msg -l | grep "custom_msgs" | wc -l -6 +4 -$ gz msg --info gz.custom_msgs.Foo +$ gz msg --info gz.custom_msgs.Foo Name: gz.custom_msgs.Foo File: gz/custom_msgs/foo.proto