Skip to content

Commit

Permalink
Updating ruby script to use standalone executable (#1489)
Browse files Browse the repository at this point in the history
* Fixed cmake to find source file for FrameSemantics
* Updated exe location in ruby script
* Add a dependency on gz-sdformat-sdf to the sdf_descriptions target
* Updated cmd line arguments in executable tests
* Added dummy flags to make tests pass

---------

Signed-off-by: Saurabh Kamat <[email protected]>
Signed-off-by: Addisu Z. Taddese <[email protected]>
Co-authored-by: Addisu Z. Taddese <[email protected]>
  • Loading branch information
sauk2 and azeey authored Jan 23, 2025
1 parent aafefa1 commit 891e490
Show file tree
Hide file tree
Showing 5 changed files with 48 additions and 267 deletions.
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})
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()
15 changes: 9 additions & 6 deletions src/cmd/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,12 @@ if (NOT HAVE_GZ_TOOLS)
endif()

# Make a small static lib of command line functions
add_library(gz STATIC gz.cc)
add_library(gz STATIC gz.cc ../FrameSemantics.cc)
target_link_libraries(gz
${PROJECT_LIBRARY_TARGET_NAME}
PUBLIC
${PROJECT_LIBRARY_TARGET_NAME}
PRIVATE
TINYXML2::TINYXML2
)

# Build sdf CLI executable
Expand Down Expand Up @@ -47,14 +50,14 @@ 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 Down Expand Up @@ -82,7 +85,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
1 change: 1 addition & 0 deletions src/cmd/sdf.bash_completion.sh
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ GZ_SDF_COMPLETION_LIST="
-k --check
-d --describe
-p --print
-g --graph
--inertial-stats
-h --help
--force-version
Expand Down
Loading

0 comments on commit 891e490

Please sign in to comment.