Skip to content
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
d823820
Implement gz sdf as standalone executable
scpeters Jun 9, 2024
ce14de5
Merge branch 'sdf15' into scpeters/gz_sdf_executable
scpeters Oct 3, 2024
aafefa1
Merge branch 'sdf15' into scpeters/gz_sdf_executable
azeey Dec 7, 2024
891e490
Updating ruby script to use standalone executable (#1489)
sauk2 Jan 23, 2025
f4fc058
Merge branch 'sdf15' into scpeters/gz_sdf_executable
azeey Jan 23, 2025
c4f155f
Moved tests and deleted redundant files
sauk2 Feb 7, 2025
756ba94
Updated cmake to build moved test
sauk2 Feb 7, 2025
193a1f1
Skipped creation of static lib for standalone exe creation
sauk2 Feb 7, 2025
699c5ae
Added includes
sauk2 Feb 7, 2025
7b27baa
Restoring previous files to save commit history
sauk2 Feb 11, 2025
9714658
Removed gz files from cmd
sauk2 Feb 11, 2025
5dae7f4
Moved cmd files by preserving history
sauk2 Feb 11, 2025
71edff4
Added namespace to cmd line hooks
sauk2 Feb 11, 2025
d834355
Changed types from int to bool for flags
sauk2 Feb 11, 2025
22d78cc
Defined test config path as alias and populated it from cmake
sauk2 Feb 11, 2025
40576fe
Merge branch 'sdf15' into gz_sdf_executable
j-rivero Feb 17, 2025
51e8485
Removed GZ_PROGRAM dependency from sdf_descriptions target
sauk2 Feb 24, 2025
b15e767
Added concise description and validation to -g option
sauk2 Feb 26, 2025
3505c3d
Removed function to print gz version
sauk2 Feb 27, 2025
eacb7b3
Added dependency on -p for print specific flags
sauk2 Feb 27, 2025
3f35036
Added back files for eventual git rename
sauk2 Feb 27, 2025
5fdfb42
Moved cmd source files
sauk2 Feb 27, 2025
0f40dd5
Added modification to cmd source files
sauk2 Feb 27, 2025
8d3b4d5
Merge branch 'sdf15' into gz_sdf_executable
sauk2 Mar 5, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion sdf/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -47,5 +47,7 @@ if (GZ_PROGRAM)
COMMENT "Generating full description for spec ${desc_ver}"
VERBATIM)
endforeach()
add_custom_target(sdf_descriptions DEPENDS ${description_targets} ${PROJECT_LIBRARY_TARGET_NAME})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was the removal of PROJECT_LIBRARY_TARGET_NAME from DEPENDS intended?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sauk2 this final question and we are good to go !

Copy link
Contributor Author

@sauk2 sauk2 Feb 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh sorry, I forgot to reply. This change was made by @azeey as a part of this commit (615048c) of this PR (#1489). He'd be the best person to clarify so tagging him here!

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we're adding a dependency on gz-sdformat-sdf below, which is ultimately what is used to generate these files, this target no longer needs to depend on PROJECT_LIBRARY_TARGET_NAME

add_custom_target(sdf_descriptions DEPENDS ${description_targets})
# Add a dependency on the gz-sdformat-sdf target which is created in in ../cmd/CMakeLists
add_dependencies(sdf_descriptions gz-sdformat-sdf)
endif()
12 changes: 0 additions & 12 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,6 @@ target_include_directories(${PROJECT_LIBRARY_TARGET_NAME}
)

if (BUILD_TESTING)
# Build this test file only if Gazebo Tools is installed.
if (NOT GZ_PROGRAM)
list(REMOVE_ITEM gtest_sources gz_TEST.cc)
endif()

add_library(library_for_tests OBJECT
Converter.cc
EmbeddedSdf.cc
Expand Down Expand Up @@ -120,13 +115,6 @@ if (BUILD_TESTING)
ENVIRONMENT
SDF_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}
)

if (TARGET UNIT_gz_TEST)
target_compile_definitions(UNIT_gz_TEST PRIVATE
-DGZ_PATH="${GZ_PROGRAM}"
-DDETAIL_GZ_CONFIG_PATH="${CMAKE_BINARY_DIR}/test/conf/$<CONFIG>"
-DGZ_TEST_LIBRARY_PATH="${PROJECT_BINARY_DIR}/src")
endif()
endif()

add_subdirectory(cmd)
48 changes: 43 additions & 5 deletions src/cmd/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,15 +1,54 @@
# Build sdf CLI executable
set(sdf_executable gz-sdformat-sdf)
add_executable(${sdf_executable} sdf_main.cc gz.cc ../FrameSemantics.cc)
target_link_libraries(${sdf_executable}
gz-utils${GZ_UTILS_VER}::cli
${PROJECT_LIBRARY_TARGET_NAME}
TINYXML2::TINYXML2
)
install(
TARGETS ${sdf_executable}
DESTINATION ${CMAKE_INSTALL_LIBEXECDIR}/gz/${GZ_DESIGNATION}${PROJECT_VERSION_MAJOR}/
)

# Build the unit tests only if Gazebo tools is installed
if(BUILD_TESTING AND GZ_PROGRAM)
gz_build_tests(TYPE UNIT
SOURCES
gz_TEST.cc
INCLUDE_DIRS
${PROJECT_SOURCE_DIR}/test
TEST_LIST
test_list
LIB_DEPS
gz-utils${GZ_UTILS_VER}::gz-utils${GZ_UTILS_VER}
${EXTRA_TEST_LIB_DEPS}
ENVIRONMENT
SDF_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}
)

if(TARGET UNIT_gz_TEST)
target_compile_definitions(UNIT_gz_TEST
PRIVATE
"GZ_PATH=\"${GZ_PROGRAM}\""
"DETAIL_GZ_CONFIG_PATH=\"${CMAKE_BINARY_DIR}/test/conf/$<CONFIG>\""
"GZ_TEST_LIBRARY_PATH=\"$<TARGET_FILE_DIR:${PROJECT_LIBRARY_TARGET_NAME}>\""
)
endif()
endif()

#===============================================================================
# Generate the ruby script for internal testing.
# Note that the major version of the library is included in the name.
# Ex: cmdsdformat0.rb
set(cmd_script_generated_test
set(cmd_script_generated_test
"${CMAKE_BINARY_DIR}/test/lib/$<CONFIG>/ruby/gz/cmd${PROJECT_NAME}.rb")
set(cmd_script_configured_test
set(cmd_script_configured_test
"${CMAKE_CURRENT_BINARY_DIR}/test_cmd${PROJECT_NAME}.rb.configured")

# Set the library_location variable to the full path of the library file within
# the build directory.
set(library_location "$<TARGET_FILE:${PROJECT_NAME}>")
set(library_location "$<TARGET_FILE:${sdf_executable}>")

configure_file(
"cmd${PROJECT_NAME_NO_VERSION_LOWER}.rb.in"
Expand All @@ -20,7 +59,6 @@ file(GENERATE
OUTPUT "${cmd_script_generated_test}"
INPUT "${cmd_script_configured_test}")


#===============================================================================
# Used for the installed version.
# Generate the ruby script that gets installed.
Expand All @@ -37,7 +75,7 @@ else()
set(library_location_prefix "${CMAKE_INSTALL_LIBDIR}")
endif()

set(library_location "../../../${library_location_prefix}/$<TARGET_FILE_NAME:${PROJECT_NAME}>")
set(library_location "../../../${CMAKE_INSTALL_LIBEXECDIR}/gz/${GZ_DESIGNATION}${PROJECT_VERSION_MAJOR}/$<TARGET_FILE_NAME:${sdf_executable}>")

configure_file(
"cmd${PROJECT_NAME_NO_VERSION_LOWER}.rb.in"
Expand Down
248 changes: 12 additions & 236 deletions src/cmd/cmdsdformat.rb.in
Original file line number Diff line number Diff line change
Expand Up @@ -14,263 +14,39 @@
# See the License for the specific language governing permissions and
# limitations under the License.

# We use 'dl' for Ruby <= 1.9.x and 'fiddle' for Ruby >= 2.0.x
if RUBY_VERSION.split('.')[0] < '2'
require 'dl'
require 'dl/import'
include DL
else
require 'fiddle'
require 'fiddle/import'
include Fiddle
end

require 'optparse'
require 'pathname'


# Constants.
LIBRARY_NAME = '@library_location@'
LIBRARY_VERSION = '@PROJECT_VERSION_FULL@'
COMMON_OPTIONS =
" -h [ --help ] Print this help message.\n"\
" --force-version <VERSION> Use a specific library version.\n"\
' --versions Show the available versions.'
COMMANDS = { 'sdf' =>
"Utilities for SDF files.\n\n"\
" gz sdf [options]\n\n"\
"Options:\n\n"\
" -k [ --check ] arg Check if an SDFormat file is valid.\n" +
" -d [ --describe ] [SPEC VERSION] Print the aggregated SDFormat spec description. Default version (@SDF_PROTOCOL_VERSION@).\n" +
" -g [ --graph ] <pose, frame> arg Print the PoseRelativeTo or FrameAttachedTo graph. (WARNING: This is for advanced\n" +
" use only and the output may change without any promise of stability)\n" +
" --inertial-stats arg Prints moment of inertia, centre of mass, and total mass from a model sdf file.\n" +
" -p [ --print ] arg Print converted arg. Note the quaternion representation of the\n" +
" rotational part of poses and unit vectors will be normalized.\n" +
" -i [ --preserve-includes ] Preserve included tags when printing converted arg (does not preserve merge-includes).\n" +
" --degrees Pose rotation angles are printed in degrees.\n" +
" --expand-auto-inertials Prints auto-computed inertial values for simple shapes. For meshes and other unsupported\n" +
" shapes, the default inertial values will be printed.\n" +
" --snap-to-degrees arg Snap pose rotation angles to this specified interval in degrees. This value must be\n" +
" larger than 0, less than or equal to 360, and larger than the defined snap tolerance.\n" +
" --snap-tolerance arg Used in conjunction with --snap-to-degrees, specifies the tolerance at which snapping\n" +
" occurs. This value must be larger than 0, less than 360, and less than the defined\n" +
" degrees value to snap to. If unspecified, its default value is 0.01.\n" +
" --precision arg Set the output stream precision for floating point numbers. The arg must be a positive integer.\n" +

COMMON_OPTIONS
}
COMMANDS = {
"sdf" => "@library_location@",
}

#
# Class for the SDF command line tools.
#
class Cmd

#
# Return a structure describing the options.
#
def parse(args)
options = {}
options['degrees'] = 0
options['expand_auto_inertials'] = 0
options['snap_tolerance'] = 0.01
options['preserve_includes'] = 0

usage = COMMANDS[args[0]]

# Read the command line arguments.
opt_parser = OptionParser.new do |opts|
opts.banner = usage

opts.on('-h', '--help", "Print this help message') do
puts usage
exit(0)
end

opts.on('-k arg', '--check arg', String,
'Check if an SDFormat file is valid.') do |arg|
options['check'] = arg
end
opts.on('--inertial-stats arg', String,
'Prints moment of inertia, centre of mass, and total mass from a model sdf file.') do |arg|
options['inertial_stats'] = arg
end
opts.on('-d', '--describe [VERSION]', 'Print the aggregated SDFormat spec description. Default version (@SDF_PROTOCOL_VERSION@)') do |v|
options['describe'] = v
end
opts.on('-p', '--print', 'Print converted arg') do
options['print'] = 1
end
opts.on('-i', '--preserve-includes', 'Preserve included tags when printing converted arg (does not preserve merge-includes)') do
options['preserve_includes'] = 1
end
opts.on('--degrees', 'Printed pose rotations are will be in degrees') do |degrees|
options['degrees'] = 1
end
opts.on('--expand-auto-inertials', 'Auto-computed inertial values will be printed') do
options['expand_auto_inertials'] = 1
end
opts.on('--snap-to-degrees arg', Integer,
'Printed rotations are snapped to specified degree intervals') do |arg|
if arg == 0 || arg > 360
puts "Degree interval to snap to must be more than 0, and less than or equal to 360."
exit(-1)
end
options['snap_to_degrees'] = arg
end
opts.on('--snap-tolerance arg', Float,
'Printed rotations are snapped if they are within this specified tolerance') do |arg|
if arg < 0 || arg > 360
puts "Rotation snapping tolerance must be more than 0, and less than 360."
exit(-1)
end
options['snap_tolerance'] = arg
end
opts.on('--precision arg', Integer,
'Set the output stream precision for floating point numbers.') do |arg|
options['precision'] = arg
end
opts.on('-g arg', '--graph type', String,
'Print PoseRelativeTo or FrameAttachedTo graph') do |graph_type|
options['graph'] = {:type => graph_type}
end
end
begin
opt_parser.parse!(args)
rescue
puts usage
exit(-1)
end

# Check that there is at least one command and there is a plugin that knows
# how to handle it.
if ARGV.empty? || !COMMANDS.key?(ARGV[0]) ||
options.empty?
puts usage
exit(-1)
end

options['command'] = ARGV[0]

if (options['preserve_includes'] != 0 and not options['print']) ||
(options['precision'] and not options['print'])
puts usage
exit(-1)
end

if options['print']
filename = args.pop
if filename
options['print'] = filename
else
puts usage
exit(-1)
end
end

options
end

#
# Execute the command
#
def execute(args)
options = parse(args)
command = args[0]
exe_name = COMMANDS[command]

# Debugging:
# puts 'Parsed:'
# puts options

# Read the plugin that handles the command.
if Pathname.new(LIBRARY_NAME).absolute?
plugin = LIBRARY_NAME
else
unless Pathname.new(exe_name).absolute?
# We're assuming that the library path is relative to the current
# location of this script.
plugin = File.expand_path(File.join(File.dirname(__FILE__), LIBRARY_NAME))
exe_name = File.expand_path(File.join(File.dirname(__FILE__), exe_name))
end
conf_version = LIBRARY_VERSION

if defined? RubyInstaller
# RubyInstaller does not search for dlls in PATH or the directory that tests are running from,
# so we'll add the parent directory of the plugin to the search path.
# https://github.com/oneclick/rubyinstaller2/wiki/For-gem-developers#-dll-loading
RubyInstaller::Runtime.add_dll_directory(File.dirname(plugin))
end

begin
Importer.dlload plugin
rescue DLError => error
puts "Library error: [#{plugin}] not found."
puts "DLError: #{error.message}"
exit(-1)
end

# Read the library version.
Importer.extern 'char* gzVersion()'
begin
plugin_version = Importer.gzVersion.to_s
rescue DLError
puts "Library error: Problem running 'gzVersion()' from #{plugin}."
exit(-1)
end
exe_version = `#{exe_name} --version`.strip

# Sanity check: Verify that the version of the yaml file matches the version
# of the library that we are using.
unless plugin_version.eql? conf_version
unless exe_version.eql? conf_version
puts "Error: Version mismatch. Your configuration file version is
[#{conf_version}] but #{plugin} version is [#{plugin_version}]."
[#{conf_version}] but #{exe_name} version is [#{exe_version}]."
exit(-1)
end

begin
case options['command']
when 'sdf'
if options.key?('check')
Importer.extern 'int cmdCheck(const char *)'
exit(Importer.cmdCheck(File.expand_path(options['check'])))
elsif options.key?('inertial_stats')
Importer.extern 'int cmdInertialStats(const char *)'
exit(Importer.cmdInertialStats(options['inertial_stats']))
elsif options.key?('describe')
Importer.extern 'int cmdDescribe(const char *)'
exit(Importer.cmdDescribe(options['describe']))
elsif options.key?('print')
snap_to_degrees = 0
precision = 0

if options.key?('snap_to_degrees')
if options['snap_to_degrees'] < options['snap_tolerance']
puts "Rotation snapping tolerance must be larger than the snapping tolerance."
exit(-1)
end
snap_to_degrees = options['snap_to_degrees']
end
if options.key?('precision')
precision = options['precision']
end
Importer.extern 'int cmdPrint(const char *, int in_degrees, int snap_to_degrees, float snap_tolerance, int, int, int)'
exit(Importer.cmdPrint(File.expand_path(options['print']),
options['degrees'],
snap_to_degrees,
options['snap_tolerance'],
options['preserve_includes'],
precision,
options['expand_auto_inertials']))
elsif options.key?('graph')
Importer.extern 'int cmdGraph(const char *, const char *)'
exit(Importer.cmdGraph(options['graph'][:type], File.expand_path(ARGV[1])))
else
puts 'Command error: I do not have an implementation '\
'for this command.'
end
else
puts 'Command error: I do not have an implementation for '\
"command [gz #{options['command']}]."
end
rescue
puts "Library error: Problem running [#{options['command']}]() "\
"from #{plugin}."
end
# Drop command from list of arguments
exec(exe_name, *args[1..-1])
end
end
Loading
Loading