class OroGen::Gen::RTT_CPP::Typekit
Support for typekit generation in oroGen
Constants
- BASE_TYPES
- BASE_TYPES_NEEDED_TRANSPORTS
- BASE_TYPES_RTT_NAMES
- COPY_METADATA_EXCLUDED_KEYS
- INCLUDE_DIR_NAME
- METADATA_RELATED_TYPES
- TYPE_EXPORT_POLICIES
The three possible type export policies. See type_export_policy and export_types
Attributes
The directory in which generated files that are meant to not be modified by the user should be saved
The base directory. Everything under that directory are expected to be local files and will be installed by the typekit's cmake code
The set of include directories that should be considered in load
The array of includes that have been loaded
Packages defined in this typekit on which the typekit should depend. See internal_dependency.
Set of directories in which the header files that have been loaded lie. It is used to generate the Cflags: line in the pkg-config file
The set of headers loaded by load, as an array of absolute paths
The typekit name
Returns the set of opaque definitions that are known by the typekit
The set of code generation plugins
The directory in which new versions of the user files should be generated. If nil, they will not be generated at all
The directory in which files that the user should modify should be saved
The typekit version string
Public Class Methods
# File lib/orogen/gen/typekit.rb, line 837 def initialize(project = nil) @project = project @include_dirs = Set.new @included_files = Array.new @plugins = [] plugins << (TypekitMarshallers::TypeInfo::Plugin.new(self)) @internal_dependencies = [] @imports, @loads = [], [] @registry = Typelib::Registry.new @imported_typekits = [] @imported_types = Typelib::Registry.new @imported_typelist = Set.new @used_libraries = [] @linked_used_libraries = [] @opaque_registry = Typelib::Registry.new @opaques = Array.new @loaded_files_dirs = Set.new @pending_load_options = [] # The order matters ! GCCXML unfortunately gave as file names # the argument to #include. So, if we are unlucky, one file # will be loaded recursively and we won't actually detect it # ... :( Not sure if this is still relevant with the # clang-based importer # # In other words, keep pending_loads an array @pending_loads = Array.new type_export_policy :all @selected_types = Set.new @excluded_types = Set.new Project.using_rtt_typekit(self) end
Register a new plugin class. The plugin name is taken from klass.name
# File lib/orogen/gen/typekit.rb, line 574 def self.register_plugin(klass) plugins[klass.name] = klass end
# File lib/orogen/gen/typekit.rb, line 878 def self.transport_plugin_name(transport_name, typekit_name) plugins.each_value do |plg| if plg.name == transport_name if plg.respond_to?(:plugin_name) return plg.plugin_name(typekit_name) else return "orogen_typekits::#{typekit_name}#{transport_name.capitalize}TransportPlugin" end end end raise ArgumentError, "invalid transport name #{transport_name}" end
Public Instance Methods
Change the directory into which the code generation should be done
# File lib/orogen/gen/typekit.rb, line 601 def automatic_dir=(path) @automatic_dir = path if path include_dirs << File.join(automatic_dir, INCLUDE_DIR_NAME) end end
Changes the typekit base directory
# File lib/orogen/gen/typekit.rb, line 591 def base_dir=(path) @base_dir = path if path include_dirs << path end end
# File lib/orogen/gen/typekit.rb, line 750 def build_type(type_name) registry.build(type_name) rescue Typelib::NotFound if type_name =~ /(.*)\[(\d+)\]$/ base_type, array_size = $1, $2 find_type(base_type, true) return registry.build(type_name) end container_name, template_arguments = Typelib::CXX.parse_template(type_name) if template_arguments.size == 1 element_type = find_type(template_arguments[0], true) if project project.registry.create_container(container_name, project.find_type(element_type.name, true)) end return registry.create_container(container_name, element_type) elsif project && type = project.registry.build(type_name) while type.respond_to?(:deference) type = type.deference end type_def = project.registry.minimal(type.name) registry.merge(type_def) return registry.build(type_name) end end
# File lib/orogen/gen/typekit.rb, line 2209 def cmake_relative_path(file, *subdir) "#{Pathname.new(file).relative_path_from(Pathname.new(File.join(automatic_dir, *subdir)))}" end
Helper method that returns the code needed to update an opaque type based on the data from an intermediate variable
# File lib/orogen/gen/typekit.rb, line 1903 def code_fromIntermediate(intermediate_type, needs_copy, indent) if intermediate_type < Typelib::ArrayType "#{indent}orogen_typekits::fromIntermediate(value, length, &intermediate[0]);\n" elsif needs_copy "#{indent}orogen_typekits::fromIntermediate(value, intermediate);\n" else "#{indent}if (orogen_typekits::fromIntermediate(value, intermediate.get()))\n" + "#{indent} intermediate.release();\n" end end
Helper method that returns the code needed to get an
intermediate variable of the right type, containing the data
in value.
# File lib/orogen/gen/typekit.rb, line 1888 def code_toIntermediate(intermediate_type, needs_copy, indent) if intermediate_type < Typelib::ArrayType "#{indent}std::vector< #{intermediate_type.deference.cxx_name} > intermediate;\n" + "#{indent}intermediate.resize(length);\n" + "#{indent}orogen_typekits::toIntermediate(&intermediate[0], value, length);\n" elsif needs_copy "#{indent}#{intermediate_type.cxx_name} intermediate;\n" + "#{indent}orogen_typekits::toIntermediate(intermediate, value);\n" else "#{intermediate_type.cxx_name} const& intermediate = orogen_typekits::toIntermediate(value);" end end
# File lib/orogen/gen/typekit.rb, line 1358 def compute_orogen_include_on_type(type, file_to_include) if includes = orogen_include_of_type(type, file_to_include) type.metadata.set('orogen_include', *includes) includes elsif has_pending_loads? perform_pending_loads compute_orogen_include_on_type(type, file_to_include) else raise ArgumentError, "cannot compute include information for #{type.name}. If it is an opaque, you must either load the header which defines it with import_types_from, or specify the relevant information with the orogen_include option" end end
# File lib/orogen/gen/typekit.rb, line 1833 def copy_metadata_to_intermediate_type(source, dest) source.each do |key, values| if key !~ /codegen/ && !COPY_METADATA_EXCLUDED_KEYS.include?(key) dest.set(key, *values) end end end
Given a set of includes, returns the code that includes them in a C++ file
@param [String] includes @return [String]
# File lib/orogen/gen/typekit.rb, line 2251 def cxx_gen_includes(*includes) includes.to_set.map do |inc| "#include <#{inc}>" end.sort.join("\n") + "\n" end
Returns the set of pkg-config packages this typekit depends on
# File lib/orogen/gen/typekit.rb, line 1667 def dependencies result = [] # We must link to libraries in case the types we are getting # from them has constructors/destructors that the library # provides. used_libraries.each do |pkg| needs_link = linked_used_libraries.include?(pkg) result << BuildDependency.new(pkg.name.upcase, pkg.name). in_context('core', 'include') if needs_link result.last.in_context('core', 'link') end end # We must link to typekits that define our types, as we are # going to reuse the convertion functions that they define used_typekits.each do |tk| next if tk.virtual? result << BuildDependency.new( tk.name.upcase + "_TYPEKIT", tk.pkg_name). in_context('core', 'include'). in_context('core', 'link') end # We must include the typekits that define types that are used # in the other typekits types each_plugin do |plg| if !plg.separate_cmake? if deps = plg.dependencies result.concat(deps) end end end result.to_a.sort_by { |dep| dep.var_name } end
# File lib/orogen/gen/typekit.rb, line 1841 def do_import(registry, path, kind, options) end
Enumerate all enabled plugins. See enable_plugin
# File lib/orogen/gen/typekit.rb, line 934 def each_plugin(&block); plugins.each(&block) end
Add a generation plugin to the generation stage
@return [Object] the newly created plugin
# File lib/orogen/gen/typekit.rb, line 894 def enable_plugin(name) if plugins.any? { |plg| plg.name == name } # It is already there return end if !(plugin = Typekit.plugins[name]) raise ArgumentError, "there is not typekit plugin called #{name}" end p = plugin.new(self) plugins << p p end
Returns an existing orogen_include metadata entry for the given type, or nil if none exists so far
@param type [Model<Type>] a type model @return [Array,nil] either a non-empty set of entries for
orogen_include or nil if none could be found
# File lib/orogen/gen/typekit.rb, line 1289 def existing_orogen_include_for_type(type) if imported_types.include?(type.name) metadata = imported_types.get(type.name).metadata if metadata.include?('orogen_include') return metadata.get('orogen_include') end end if self.registry.include?(type.name) metadata = self.registry.get(type.name).metadata if metadata.include?('orogen_include') return metadata.get('orogen_include') end end metadata = type.metadata if metadata.include?('orogen_include') return metadata.get('orogen_include') end nil end
Select a set of types to be exported through the RTT type system, instead of exporting everything. This is meant to reduce the typekit's code size and compilation times tremendously.
See also type_export_policy
# File lib/orogen/gen/typekit.rb, line 675 def export_types(*selection) @selected_types |= selection.map { |name| find_type(name) }.to_set rescue Typelib::NotFound => e raise ConfigError, e.message, e.backtrace end
Returns true if typename can be used on a task context
interface
# File lib/orogen/gen/typekit.rb, line 744 def exported_type?(typename) imported_typekits_for(typename).any? do |tk| tk.interface_type?(typename) end end
# File lib/orogen/gen/typekit.rb, line 1227 def filter_unsupported_types(registry) to_delete = Set.new registry.each do |type| # Multi-dimensional arrays are forbidden in CORBA IDL # TODO: work around this in Typelib and oroGen by emitting a # TODO: single-dimension array of the right size if type < Typelib::ArrayType && type.deference < Typelib::ArrayType RTT_CPP.warn "ignoring #{type.name} as multi-dimensional arrays cannot be represented in CORBA IDL" to_delete << type elsif type < Typelib::PointerType RTT_CPP.warn "ignoring #{type.name} as pointers are not allowed" to_delete << type elsif type.name == "/std/vector</bool>" RTT_CPP.warn "std::vector<bool> is unsupported in oroGen due to its special nature. Use std::vector<uint8_t> instead." to_delete << type elsif type < Typelib::CompoundType type.each_field do |field_name, _| if field_name !~ /^[a-zA-Z]/ RTT_CPP.warn "ignoring #{type.name} as its field #{field_name} does not start with an alphabetic character, which is forbidden in CORBA IDL" to_delete << type break end end end end to_delete.each do |type| deleted_types = registry.remove(type) deleted_types.each do |dep_type| next if to_delete.include?(dep_type) RTT_CPP.warn "ignoring #{dep_type.name} as it depends on #{type.name} which is ignored" end end end
Looks for an enabled plugin called name
@param [String] name the name of the plugin to look for @return [Object] the plugin object, or nil if it cannot be found @see plugin
# File lib/orogen/gen/typekit.rb, line 914 def find_plugin(name) return plugins.find { |p| p.name == name } end
Returns the Typelib::Type subclass that represents the type whose name is given. If the type is a derived type (pointer, array or container), then it will be built on the fly.
# File lib/orogen/gen/typekit.rb, line 782 def find_type(type, is_normalized = false) if type.respond_to?(:to_str) type_name = type.gsub('::', '/') if !is_normalized type_name = Typelib::Type.normalize_typename(type_name) is_normalized = true end begin registry.get(type_name) rescue Typelib::NotFound new_type = build_type(type_name) compute_orogen_include_on_type(new_type, Hash.new) return new_type end elsif type.kind_of?(Class) && type <= Typelib::Type if !registry.include?(type.name) type_def = type.registry.minimal(type.name) registry.merge(type_def) if project project.registry.merge(type_def) end new_type = registry.get(type.name) compute_orogen_include_on_type(new_type, Hash.new) return new_type else return registry.get(type.name) end else raise ArgumentError, "expected a type object or a type name, but got #{type} (#{type.class})" end rescue Typelib::NotFound => e if !pending_loads.empty? perform_pending_loads retry end raise e.class, e.message, e.backtrace end
# File lib/orogen/gen/typekit.rb, line 1974 def generate typekit = self FileUtils.mkdir_p automatic_dir # Populate a fake installation directory so that the include # files can be referred to as <project_name>/header.h fake_install_dir = File.join(automatic_dir, name) # Upgrade from directory to symlink if !File.symlink?(fake_install_dir) FileUtils.rm_rf fake_install_dir end Generation.create_or_update_symlink(automatic_dir, fake_install_dir) if standalone? fake_typekit_dir = File.join(automatic_dir, "typekit") Generation.create_or_update_symlink(automatic_dir, fake_typekit_dir) end # Generate opaque-related stuff first, so that we see them in # the rest of the typelib-registry-manipulation code handle_opaques_generation(registry) perform_pending_loads # Do some registry mumbo-jumbo to remove unneeded types to the # dumped registry @registry = normalize_registry minimal_registry = @registry.dup generated_types = self_types.to_set self_opaques = self.self_opaques.sort_by { |opdef| opdef.type.name } issue_warnings(generated_types, minimal_registry) generate_typedefs(generated_types, minimal_registry) # The first array is the set of types for which convertion # functions are generated. The second is the set of types that # are actually registered into the RTT type system # # The two arrays are sorted so that we don't have to recompile # unnecessarily (the original sets are hashes, and therefore don't # have a stable order). converted_types = generated_types. find_all { |type| !(type <= Typelib::NumericType) }. sort_by { |type| type.name } # We need a special case for arrays. The issue is the following: # for arrays, the convertion functions take pointers as input. # So we generate only one convertion function for all arrays of # the same type, regardless of the size. converted_types, array_types = converted_types. partition { |type| !(type < Typelib::ArrayType) } array_types = array_types. delete_if do |type| imported_array_of?(type.deference) end. inject(Hash.new) { |h, type| h[type.deference.name] = type; h }. values. sort_by { |type| type.name } # If the selected type export policy is used, check if it makes # sense. If it does not, switch back to 'all' if type_export_policy == :used && (!project || project.self_tasks.empty?) type_export_policy :all end # Get the types that should be registered in the RTT type # system. Note that we must map these types to +registry+ as # +registry+ has been recomputed (i.e. it changes between the # specification time and the generation time) # # Only +generated_types+ does not need to be mapped as it has # just been computed registered_types = if type_export_policy == :all generated_types.find_all do |type| !m_type?(type) && !(type <= Typelib::NumericType) end.to_set elsif type_export_policy == :used used_types = project.self_tasks.inject(Set.new) do |result, task| result | map_typeset_to_registry(registry, task.interface_types) end (used_types & generated_types) elsif type_export_policy == :selected map_typeset_to_registry(registry, selected_types) end if !selected_types.empty? registered_types |= map_typeset_to_registry(registry, selected_types) end # Save all the types that this specific typekit handles, # including aliases registered_typenames = registered_types.map(&:name).to_set all_names = Hash.new minimal_registry.each(:with_aliases => true) do |name, type| all_names[type.name] ||= [] all_names[type.name] << name end typelist_txt = [] generated_types.each do |type| is_exported = registered_typenames.include?(type.name) ? '1' : '0' metadata = minimal_registry.get(type.name).metadata metadata.add 'orogen_defining_typekits', self.name if is_exported metadata.add 'orogen_exporting_typekits', self.name end all_names[type.name].each do |type_name| typelist_txt << "#{type_name} #{is_exported}" end end save_automatic "#{name}.typelist", typelist_txt.sort.join("\n") # Generate the XML representation of the generated type library, # and add opaque information to it plain_registry = minimal_registry.to_xml doc = REXML::Document.new(plain_registry) doc.each_element('//opaque') do |opaque_entry| spec = opaque_specification(opaque_entry.attributes['name']) opaque_entry.add_attributes( 'marshal_as' => spec.intermediate, 'includes' => spec.includes.join(':'), 'needs_copy' => (spec.needs_copy? ? '1' : '0')) end modified_tlb = String.new doc.write(modified_tlb) save_automatic "#{name}.tlb", modified_tlb registered_types = registered_types. sort_by { |t| t.name } registered_types.delete_if do |t| t.contains_opaques? && t < Typelib::ArrayType end interface_types = registered_types. find_all { |t| !(t < Typelib::ArrayType) } type_sets = TypeSets.new type_sets.types = generated_types.sort_by { |t| t.name } type_sets.converted_types = converted_types type_sets.array_types = array_types type_sets.registered_types = registered_types type_sets.interface_types = interface_types type_sets.minimal_registry = minimal_registry type_sets.opaque_types = self_opaques type_sets.aliases = Hash.new public_header_files, plugin_header_files, implementation_files = [], [], [] # For backward compatibility if File.symlink?(old_symlink = File.join(automatic_dir, 'Opaques.hpp')) FileUtils.rm_f old_symlink end type_header = Generation.render_template('typekit/Types.hpp', binding) public_header_files << save_automatic_public_header("Types.hpp", type_header) type_header = Generation.render_template('typekit/TypesDeprecated.hpp', binding) save_automatic("Types.hpp", type_header) type_header = Generation.render_template('typekit/OpaquesDeprecated.hpp', binding) save_automatic("Opaques.hpp", type_header) boost_serialization = Generation.render_template "typekit/BoostSerialization.hpp", binding public_header_files << save_automatic_public_header("BoostSerialization.hpp", boost_serialization) tk_hpp = Generation.render_template "typekit/Plugin.hpp", binding public_header_files << save_automatic_public_header("Plugin.hpp", tk_hpp) tk_cpp = Generation.render_template "typekit/Plugin.cpp", binding implementation_files << save_automatic("Plugin.cpp", tk_cpp) # Generate the opaque convertion files if has_opaques? intermediates_hpp = Generation.render_template 'typekit/OpaqueConvertions.hpp', binding public_header_files << save_automatic_public_header("OpaqueConvertions.hpp", intermediates_hpp) intermediates_cpp = Generation.render_template 'typekit/OpaqueConvertions.cpp', binding implementation_files << save_automatic("OpaqueConvertions.cpp", intermediates_cpp) fwd_hpp = Generation.render_template 'typekit/OpaqueFwd.hpp', binding public_header_files << save_automatic_public_header("OpaqueFwd.hpp", fwd_hpp) types_hpp = Generation.render_template 'typekit/OpaqueTypes.hpp', binding public_header_files << save_automatic_public_header("OpaqueTypes.hpp", types_hpp) if has_opaques_with_templates? user_hh = Generation.render_template 'typekit/Opaques.hpp', binding user_cc = Generation.render_template 'typekit/Opaques.cpp', binding save_user("Opaques.hpp", user_hh) implementation_files << save_user("Opaques.cpp", user_cc) Generation.create_or_update_symlink( File.join(user_dir, "Opaques.hpp"), File.join(automatic_dir, INCLUDE_DIR_NAME, self.name, 'typekit', "Opaques.hpp")) end end each_plugin do |plg| plg_typesets = type_sets.dup headers, impl = plg.generate(plg_typesets) plugin_header_files.concat(headers) implementation_files.concat(impl) end if standalone? FileUtils.mkdir_p File.join(automatic_dir, 'config') Dir.glob File.join(Generation.template_path('config'), '*') do |path| basename = File.basename(path) if !Project::CMAKE_GENERATED_CONFIG.include?(basename) save_automatic 'config', basename, File.read(path) end end end pkg_config = Generation.render_template 'typekit/typekit.pc', binding save_automatic("#{name}-typekit.pc.in", pkg_config) cmake = Generation.render_template 'typekit', 'CMakeLists.txt', binding save_automatic("CMakeLists.txt", cmake) manifest = Generation.render_template 'typekit', 'manifest.xml', binding save_automatic("manifest.xml", manifest) package = Generation.render_template 'typekit', 'package.xml', binding save_automatic("package.xml", package) makefile = Generation.render_template 'typekit', 'Makefile', binding save_automatic("Makefile", makefile) # Finished, create the timestamp file Generation.cleanup_dir(automatic_dir) Generation.touch File.join(automatic_dir, 'stamp') end
This generates typedefs for container types. These typedefs are needed because IDL and the CORBA C++ mapping do not allow to reference sequence types directly (you have to typedef them first, or use them in a compound)
# File lib/orogen/gen/typekit.rb, line 1848 def generate_typedefs(generated_types, registry) end
Handle a load that points to a file in this typekit's source tree
# File lib/orogen/gen/typekit.rb, line 1133 def handle_local_load(file) rel = Pathname.new(file).relative_path_from(Pathname.new(base_dir)) return file if rel.each_filename.first == ".." local_type_dir = Pathname.new(includes_dir) rel_to_type_dir = Pathname.new(file).relative_path_from(local_type_dir) if rel_to_type_dir.each_filename.first == ".." # If the type is within a subdirectory called as the # typekit, remove the duplicate elements = rel.each_filename.to_a if elements.first != self.name elements.unshift self.name end target = File.join(includes_dir, *elements) Generation.create_or_update_symlink(file, target) target else file end end
# File lib/orogen/gen/typekit.rb, line 1773 def handle_opaques_generation(generated_types) # Make sure all opaque intermediate types are existing or can be # instanciated self_opaques.each do |opaque_def| begin t = find_type(opaque_def.intermediate) copy_metadata_to_intermediate_type(opaque_def.type.metadata, t.metadata) if t.contains_opaques? raise ConfigError, "the type #{opaque_def.intermediate} is used as an intermediate type for #{opaque_def.type.name}, but it is an opaque or contains opaques" end rescue Typelib::NotFound raise ConfigError, "type #{opaque_def.intermediate}, used as intermediate for opaque #{opaque_def.type.name}, does not exist", opaque_def.caller end end # Generate some type definitions for the pocosim marshalling. In # practice, we generate C code that we merge back into the # repository needed_definitions = self_types. find_all { |t| t.contains_opaques? && !t.opaque? } # Sort the types by the order in which we should generate their # intermediates. This is needed when one m-type requires another # m-type that we generate in this typekit as well to_generate = [] while !needed_definitions.empty? needed_definitions.delete_if do |type| next(true) if m_type_exists?(type) next if type.dependencies.any? { |t| needed_definitions.include?(t) } to_generate << type end end options = { :include => include_dirs.dup } to_generate.each do |type| needed_type_definitions = type.direct_dependencies.map do |needed_type| find_type(intermediate_type_for(needed_type), true) end typekit = self marshalling_code = Generation. render_template 'typekit', 'marshalling_types.hpp', binding path = Generation.save_automatic 'typekit', 'types', self.name, "m_types", "#{type.method_name(true)}.hpp", marshalling_code self.load(path, true, options) m_type = intermediate_type_for(type) copy_metadata_to_intermediate_type(type.metadata, m_type.metadata) if type.respond_to?(:field_metadata) && m_type.respond_to?(:field_metadata) m_type.field_metadata.each do |field_name, field_metadata| copy_metadata_to_intermediate_type(type.field_metadata[field_name], m_type.field_metadata[field_name]) end end m_type.metadata.set 'orogen:m_type', '1' m_type.metadata.add 'orogen:intermediate_for', type.name end true end
True if there are some opaques in this typekit. The result of this method is only valid during generation. Don't use it in general.
# File lib/orogen/gen/typekit.rb, line 1123 def has_opaques? self_types.any? { |t| t.contains_opaques? } end
True if some opaques require to generate templates
# File lib/orogen/gen/typekit.rb, line 1128 def has_opaques_with_templates? self_opaques.any? { |op| op.generate_templates? } end
# File lib/orogen/gen/typekit.rb, line 1488 def has_pending_loads? !pending_loads.empty? end
# File lib/orogen/gen/typekit.rb, line 1583 def import(other_typekit) raise NotImplementedError end
# File lib/orogen/gen/typekit.rb, line 1625 def imported_array_of?(type) typename = if type.respond_to?(:name) then type.name else type.to_str end imported_typelist.any? { |str| str =~ /#{Regexp.quote(typename)}(\[\d+\])+/ } end
Returns true if typename has been defined by a typekit
imported by #using_typekit
# File lib/orogen/gen/typekit.rb, line 739 def imported_type?(typename) !selected_types.find { |t| t.name == typename } && !imported_typekits_for(typename).empty? end
Returns the typekit object that defines this type
# File lib/orogen/gen/typekit.rb, line 730 def imported_typekits_for(typename) if typename.respond_to?(:name) typename = typename.name end return imported_typekits.find_all { |tk| tk.includes?(typename) } end
Returns the set of includes that should be added to get access to the given type
@param [Model<Typelib::Type>] type the type subclass @return [Array<String>] @see #cxx_gen_includes
# File lib/orogen/gen/typekit.rb, line 2233 def include_for_type(type) if includes = existing_orogen_include_for_type(type) return includes.map { |s| s.split(':').last } end if type.opaque? raise ConfigError, "no includes known for #{type.name}, This is an opaque, and you must either call import_types_from on a header that defines it, or provide the :include option to the opaque definition" else raise InternalError, "no includes known for #{type.name} defined in #{type.metadata.get("source_file_line")}" end end
Full path to the directory in which includes are either generated or symlinked
# File lib/orogen/gen/typekit.rb, line 610 def includes_dir File.join(automatic_dir, INCLUDE_DIR_NAME) end
# File lib/orogen/gen/typekit.rb, line 1707 def intermediate_cxxname_for(type_def) type = find_type(type_def) if type < Typelib::ArrayType prefix, suffix = intermediate_cxxname_for(type.deference) [prefix, "#{suffix}[#{type.length}]"] else name = intermediate_type_name_for(type) [name.gsub('/', '::').gsub('<::', '< ::').gsub('>>','> >')] end end
The second one allows to specify a dependency of the typekit on a library defined in the same CMake package. The typekit's .pc file will therefore depend on the specified pkg-config package (among other things). Do that when you define a library of types and want people to be able to use it even though they don't have orogen.
In other words, it allows to build packages were:
* a normal C/C++ library is defined/built/install * an orogen typekit is defined, which wraps the types defined by this library
# File lib/orogen/gen/typekit.rb, line 1579 def internal_dependency(name, version = nil) @internal_dependencies << [name, version] end
# File lib/orogen/gen/typekit.rb, line 1851 def issue_warnings(generated_types, registry) generated_types.each do |type| if type.contains_int64? OroGen::Gen::RTT_CPP.info "you will not be able to marshal #{type.name} as XML, it contains 64bit integers" end end end
True if the orocos target is gnulinux
# File lib/orogen/gen/typekit.rb, line 828 def linux?; orocos_target == 'gnulinux' end
Load the types defined in the specified file.
file may contain pure-C code with the following C++ additions:
-
namespaces
-
std::vector< type >. You have to specifystd::vector(and not simplyvector, as theusing namespacedirective is not supported by orogen.
Moreover, the orogen tool defines the __orogen preprocessor
symbol when it loads the file. It is therefore possible to define
constructors, destructors, operators and (more generically) methods by
enclosing them in a block like
#ifndef __orogen ... C++ code not supported by orogen ... #endif
The use of virtual methods and inheritance is completely forbidden. The types need to remain “value types” without inheritance. For those who want to know, this is needed so that orogen is able to compute the memory layout of the types (i.e. the exact offsets for all the fields in the structures).
@raises LoadError if the file does not exist
# File lib/orogen/gen/typekit.rb, line 1179 def load(file, add = true, user_options = Hash.new) if !user_options.respond_to?(:to_hash) raise ArgumentError, "expected an option has as third argument, got #{user_options.inspect}" end if match = /(\w+)\/Types\.hpp$/.match(file) project_name = match[1] if project.has_typekit?(project_name) || project.name == project_name raise ArgumentError, "cannot use a header called #{file} as it clashes with the Types.hpp header generated by orogen for the #{project_name} project" end end include_dirs = self.include_dirs # Get the full path for +file+ file = if File.file?(file) # Local file File.expand_path(file) else # File from used libraries/task libraries dir = include_dirs.find { |dir| File.file?(File.join(dir, file)) } if !dir raise LoadError, "cannot find #{file} in #{include_dirs.to_a.join(":")}" end loaded_files_dirs << dir File.join(dir, file) end # If it is a local header, symlink it to the "right" place in # typekit/types and load that file = handle_local_load(file) # And resolve it to an include statement include_path = include_dirs.map { |d| Pathname.new(d) } inc = resolve_full_include_path_to_relative(file, include_path) included_files << inc user_options[:rawflags] = self.used_libraries. map { |lib| lib.raw_cflags_only_other }. flatten.uniq this_options = [add, user_options] if pending_load_options != this_options perform_pending_loads end @pending_load_options = this_options pending_loads << file end
# File lib/orogen/gen/typekit.rb, line 1723 def m_type_exists?(type) target_typename = intermediate_type_name_for(type) current_def = begin find_type(target_typename) rescue Typelib::NotFound end return false if !current_def expected_type = type while current_def.respond_to?(:deference) current_def = current_def.deference if !expected_type.respond_to?(:deference) raise "#{current_def.name} is already defined, but does not match the expected definition. Did you define it yourself ?" end expected_type = expected_type.deference end if current_def < Typelib::CompoundType if expected_type.opaque? # nothing to do elsif !(expected_type < Typelib::CompoundType) raise "#{current_def.name} is already defined, but does not match the expected definition. Did you define it yourself ?" elsif current_def.fields.size != expected_type.fields.size raise "#{current_def.name} is already defined, but does not match the expected definition. Did you define it yourself ?" else type_fields = expected_type.fields.dup current_fields = current_def.fields.dup while !type_fields.empty? expected = type_fields.first current = current_fields.first if expected[0] != current[0] || !expected[1].opaque? && expected[1] != current[1] raise "#{current_def.name} is already defined, but does not match the expected definition. Did you define it yourself ?" elsif expected[1].opaque? if intermediate_type_for(expected[1]) != current[1] raise "#{current_def.name} is already defined, but does not match the expected definition. Did you define it yourself ?" end end type_fields.pop current_fields.pop end end end true end
# File lib/orogen/gen/typekit.rb, line 1719 def m_types_code @m_types_code end
# File lib/orogen/gen/typekit.rb, line 1451 def make_load_options(pending_loads, user_options) options = { :opaques_ignore => true, :merge => false, :required_files => pending_loads.to_a } # GCCXML can't parse vectorized code, and the Typelib internal # parser can't parse eigen at all. It is therefore safe to do it # here options[:define] = ["OROCOS_TARGET=#{RTT_CPP.orocos_target}", '__orogen2'] options[:include] = self.include_dirs.dup options = options.merge(user_options) do |key, a, b| if a.respond_to?(:to_ary) if b.respond_to?(:to_ary) b.concat(a) else [b].concat(a) end else b end end opaque_names = opaques.map { |opdef| opdef.type.name } options[:opaques] = opaque_names # In principle, the import stage should not need any of the # include and defines since it is given the preprocessed file. # However, the clang importer breaks this and re-preprocesses # the file. # # This works around it by passing the flags to both stages. preprocess_options = Hash.new preprocess_options[:rawflags] = options.fetch(:rawflags, Array.new) preprocess_options[:include] = options.fetch(:include, Array.new) preprocess_options[:include_paths] = options.fetch(:include, Array.new) preprocess_options[:define] = options.fetch(:define, Array.new) return preprocess_options, options end
Makes sure that a set of type objects comes from the same registry
In order to use Set, we must make sure that all type objects come from the
same registry. This method takes a Set of types and converts all of them to
the type coming from registry
# File lib/orogen/gen/typekit.rb, line 1970 def map_typeset_to_registry(registry, types) types.map { |t| find_type(t) }.to_set end
# File lib/orogen/gen/typekit.rb, line 1592 def normalize_registry base = self.registry.dup # Properly define the headers we want to use for cstdint headers [1, 2, 4, 8].each do |int_size| if base.include?("/int#{int_size * 8}_t") base.get("/int#{int_size * 8}_t"). metadata.set("orogen_include", "boost/cstdint.hpp") end if base.include?("/uint#{int_size * 8}_t") base.get("/uint#{int_size * 8}_t"). metadata.set("orogen_include", "boost/cstdint.hpp") end end result = Typelib::Registry.new self_types.each do |type| result.merge(base.minimal(type.name)) end # Also register the intermediate types for our opaques. The # resulting registry must be self-contained, and the # intermediate stuff is specific to oroGen (i.e. typelib does # not handle them for us) opaques.each do |op_def| if result.include?(op_def.type.name) result.merge(base.minimal(find_type(op_def.intermediate).name)) end end result end
Declare that the user will provide a method which converts
base_type into the given intermediate type. Orogen will then
use that intermediate representation to marshal the data.
Orogen has a specific support for data held by smart pointers. See smart_ptr.
The following options are available:
:includes is an optional set of headers needed to define
base_type. For instance:
opaque_type "Eigen::Vector3f", "imu::Vector3f", :includes => ["/usr/include/eigen2", "/opt/boost/include"]
If there is only one include, the array can be omitted
opaque_type "Eigen::Vector3f", "imu::Vector3f", :includes => "/usr/include/eigen2"
:needs_copy is a flag telling how the opaque type should be
converted. If true (the default), orogen will generate convertion methods
whose signature are:
void orogen_typekits::toIntermediate(intermediate_type& intermediate, base_type const& sample) void orogen_typekits::fromIntermediate(base_type& sample, intermediate_type const& intermediate)
In the first examples above, this would be (assuming an “imu” orogen project)
void orogen_typekits::toIntermediate(imu::Vector3f& intermediate, Eigen::Vector3f const& sample) void orogen_typekits::fromIntermediate(Eigen::Vector3f& sample, imu::Vector3f& intermediate)
Note that in fromIntermediate, the intermediate argument in
non-const. It is actually allows to modify it, as for instance to get
better performance (example: use +vector<>.swap()+ instead of doing a
big copy).
If :needs_copy is false, then we assume that a copy is not
needed. In that case, the toIntermediate convertion method will return the
intermediate type directly. The signature will therefore be changed to
intermediate_type& orogen_typekits::toIntermediate(base_type const& sample) bool orogen_typekits::fromIntermediate(base_type& sample, intermediate_type* intermediate)
Note that in fromIntermediate the intermediate argument is now
given as a non-const pointer. The convertion function can choose to take
ownership on that value, in which case it has to return true. If the
function returns false, then the sample is deleted after the method call.
# File lib/orogen/gen/typekit.rb, line 1077 def opaque_type(base_type, intermediate_type, options = Hash.new, &convert_code_generator) options = Kernel.validate_options options, :include => [], :includes => [], :orogen_include => [], :needs_copy => true if options[:includes].respond_to?(:to_str) options[:includes] = [options[:includes]] end if options[:include].respond_to?(:to_str) options[:include] = [options[:include]] end options[:include].concat(options.delete(:includes)) base_type = base_type.to_str base_type = Typelib::Type.normalize_typename(base_type) if intermediate_type.kind_of?(Class) && intermediate_type < Typelib::Type intermediate_type = intermediate_type.name else intermediate_type = Typelib::Type.normalize_typename(intermediate_type) end typedef = "<typelib><opaque name=\"#{base_type.gsub('<', '<').gsub('>', '>')}\" size=\"#{0}\" /></typelib>" opaque_def = Typelib::Registry.from_xml(typedef) opaque_registry.merge opaque_def registry.merge opaque_def if project project.registry.merge opaque_def end opaque_type = find_type(base_type, true) options[:include].each do |inc| opaque_type.metadata.add('orogen_include', "#{self.name}:#{inc}") end orogen_def = OpaqueDefinition.new(opaque_type, intermediate_type, options, convert_code_generator) orogen_def.caller = caller @opaques << orogen_def @opaques = opaques. sort_by { |orogen_def| orogen_def.type.name } opaque_type end
The target operating system for orocos. Uses the OROCOS_TARGET environment variable, if set, and defaults to gnulinux otherwise.
# File lib/orogen/gen/typekit.rb, line 823 def orocos_target RTT_CPP.orocos_target.dup end
# File lib/orogen/gen/typekit.rb, line 1309 def orogen_include_of_type(type, file_to_include) # 'Types with deference' are vectors and arrays. Arrays # are ignored. Vectors are hardcoded to :vector if type.respond_to?(:deference) deference_includes = orogen_include_of_type(type.deference, file_to_include) # if the type used in the opaque/ro_ptr/container has no # include, the container doesn't need one as well? if !deference_includes return end container_includes = [] if type <= Typelib::ContainerType if type.name == '/std/string' container_includes = [':string'] elsif type.container_kind == '/std/vector' container_includes = [':vector'] else raise ArgumentError, "unexpected container type #{type.container_kind}" end end return container_includes + deference_includes elsif type <= Typelib::NullType return [] elsif type <= Typelib::NumericType if type.integer? then return [':boost/cstdint.hpp'] else return [] end elsif existing = existing_orogen_include_for_type(type) return existing else if !(location = type.metadata.get('source_file_line').first) return end file, line = location.split(':') if !File.file?(file) RTT_CPP.debug("resolve_registry_includes: deleting non-existing 'line' entry in metadata 'source_file_line'=#{location}") type.metadata.delete('source_file_line') return end if orogen_include = file_to_include[file][Integer(line)] return ["#{self.name}:#{orogen_include}"] else raise ArgumentError, "no entry for '#{file}:#{line}' in the provided file-to-include mapping of typekit '#{self.name}'" end end end
# File lib/orogen/gen/typekit.rb, line 1492 def perform_pending_loads return if pending_loads.empty? loads = pending_loads.dup pending_loads.clear add, user_options = *pending_load_options include_dirs = self.include_dirs.to_a if automatic_dir include_dirs << File.join(automatic_dir, "types") end file_registry = Typelib::Registry.new file_registry.merge opaque_registry preprocess_options, options = make_load_options(loads, user_options) preprocessed, include_mappings = resolve_toplevel_include_mapping(loads, preprocess_options) include_path = include_dirs.map { |d| Pathname.new(d) } pending_loads_to_relative = loads.inject(Hash.new) do |map, path| map[path] = resolve_full_include_path_to_relative(path, include_path) map end include_mappings.each do |file, lines| lines.map! { |inc| pending_loads_to_relative[inc] } end Tempfile.open(["orogen-pending-loads_",".hpp"]) do |io| io.write preprocessed io.flush begin file_registry.import(io.path, 'c', options) filter_unsupported_types(file_registry) resolve_registry_includes(file_registry, include_mappings) validate_related_types(file_registry) registry.merge(file_registry) if project project.registry.merge(file_registry) end rescue Exception => e raise ArgumentError, "cannot load one of the header files #{loads.join(", ")}: #{e.message}", e.backtrace end end if add self.loads.concat(loads.to_a) end end
Return the plugin object for name
@param [String] name the name of the plugin to look for @return [Object]
the plugin object, or nil if it cannot be found @raise [ArgumentError]
raised if no plugin is enabled with name name @see #find_plugin
# File lib/orogen/gen/typekit.rb, line 925 def plugin(name) if plg = find_plugin(name) return plg else raise ArgumentError, "there is no plugin called #{name}" end end
# File lib/orogen/gen/typekit.rb, line 1938 def render_typeinfo_snippets(code_snippets, *place) return [] if code_snippets.empty? impl = [] place = File.join(*place) code_snippets.each do |code| if code[0].respond_to?(:name_as_word) code[0] = code[0].name_as_word else code[0] = code[0].to_str end end code_snippets = code_snippets.sort_by { |code| code[0] } slice_size = RTT_CPP.typekit_slice while slice_size > 1 && code_snippets.size / slice_size < RTT_CPP.typekit_slice_minimum slice_size -= 1 end code_snippets.each_slice(slice_size) do |code| file_name = code.map(&:first).join("_") code = code.map(&:last).join("\n") impl << save_automatic(place, "#{file_name}.cpp", code) end impl end
Resolves a full path into the one that should be used when taking into account the include path
@param [String] file the full path to the include file @param [Array<Pathname>] include_path the set of include dirs @return [String]
# File lib/orogen/gen/typekit.rb, line 1271 def resolve_full_include_path_to_relative(file, include_path) file = Pathname.new(file) # Find the base include include_candidates = include_path.map do |inc| rel = file.relative_path_from(inc).cleanpath.to_path if rel !~ /^\.\.\// rel end end include_candidates.compact.min_by { |inc| inc.size } end
Resolves the orogen_include metadata for each type in the given registry
It computes the set of include <> statements that are required to get access on the C++ side to each type in the registry. It is saved in the orogen_include metadata information of the types.
@param [Typelib::Registry] registry whose types should be
resolved
@param [{String=>Array<String>}] file_to_include mapping from a
source file and line to the toplevel include that relates to this file
# File lib/orogen/gen/typekit.rb, line 1381 def resolve_registry_includes(registry, file_to_include) queue = Array.new registry.each do |type| _, template_args = Typelib::CXX.parse_template(type.name) template_args = template_args.map do |type_name| if registry.include?(type_name) registry.get(type_name) end end.compact queue << [type, template_args] end queue = queue.sort_by { |type, template_args| [template_args.size, type.name.size] } while !queue.empty? # If this is a template, we need to add the relevant # includes for the parameters as well. We need to do some # form of ordering for that to work ... queue.delete_if do |type, template_args| has_unresolved_args = template_args.any? do |template_arg_type| queue.include?(template_arg_type) end if !has_unresolved_args compute_orogen_include_on_type(type, file_to_include) true end end end end
# File lib/orogen/gen/typekit.rb, line 1410 def resolve_toplevel_include_mapping(toplevel_files, options) preprocessed = Typelib::CXX.preprocess(toplevel_files, 'c', options) owners = Hash.new { |h,k| h[k] = Array.new } current_file = [[]] preprocessed.each_line do |line| if line =~ /# (\d+) "(.*)"(?: (\d))?/ lineno, file, mode = Integer($1), $2, $3 if mode == "1" toplevel_file = if toplevel_files.include?(file) then file else current_file.last[0] end # the gccxml-importer always reported "flattened" # filepath, like "/usr/include/c++/4.9/bits". # clang++ does print # "/usr/lib/gcc/x86_64-linux-gnu/4.9/../../../../include/c++/4.9/bits/stl_vector.h" # into the preprocessed output (read here), so we # have to flattend the path. this can fail for # "built-in" for examples, thus the rescue. begin current_file.push [toplevel_file, File.realpath(file), lineno] rescue Errno::ENOENT # now file is smth like "<built-in>" or # similar, at it anyways... current_file.push [toplevel_file, file, lineno] end elsif mode == "2" current_file.pop end current_file.last[2] = lineno else toplevel, file, lineno = *current_file.last owners[file][lineno] = toplevel current_file.last[2] += 1 end end return preprocessed, owners end
Make the typekit define the specialization of RTT::ReadOnlyPointer for the given type.
See smart_ptr for more information.
# File lib/orogen/gen/typekit.rb, line 1012 def ro_ptr(name, options = Hash.new) options[:orogen_include] ||= Array.new options[:orogen_include] << "orocos-rtt-#{RTT_CPP.orocos_target}:rtt/extras/ReadOnlyPointer.hpp" smart_ptr("/RTT/extras/ReadOnlyPointer", find_type(name), options) end
# File lib/orogen/gen/typekit.rb, line 1922 def save_automatic(*args) Generation.save_generated(true, automatic_dir, *args) end
# File lib/orogen/gen/typekit.rb, line 1916 def save_automatic_public_header(*args) rel = File.join(self.name, 'typekit', *args[0..-2]) Generation.save_generated(true, automatic_dir, INCLUDE_DIR_NAME, rel, args[-1]) rel end
# File lib/orogen/gen/typekit.rb, line 1926 def save_user(*args) path = Generation.save_generated(false, user_dir, *args) if templates_dir Generation.save_generated(true, templates_dir, *args) end path end
Returns the set of opaques that have been added to this particular typekit
# File lib/orogen/gen/typekit.rb, line 697 def self_opaques opaques.find_all { |opdef| !imported_type?(opdef.type) } end
Returns the set of type names defined in this typekit. This is different from #self_types, as it returns a set of type names (i.e. strings), and also because it includes the aliases defined by the typekit.
# File lib/orogen/gen/typekit.rb, line 1863 def self_typenames generated_types = [] registry.each(:with_aliases => true) do |name, type| next if imported_type?(name) if !type.inlines_code? generated_types << name end end generated_types end
Returns the set of types that are specifically handled by this typekit
# File lib/orogen/gen/typekit.rb, line 1876 def self_types generated_types = [] registry.each do |type| next if imported_type?(type.name) generated_types << type end generated_types end
Ask to support a smart-pointer implementation on the given type. For instance, after the call
smart_ptr "boost::smart_ptr", "int"
You will be able to use the following type in your task definitions:
boost::smart_ptr_name<T>
The smart pointer class needs to meet the following requirements:
* the memory should be automatically managed (allocated/deallocated) * the star operator can be used to get a const reference on the managed value (i.e. <tt>*pointer</tt> returns the object) * the smart pointer must define a #reset method to set a new managed memory zone.
The #ro_ptr and #shared_ptr shortcuts are defined for boost::shared_ptr and RTT::ReadOnlyPointer.
# File lib/orogen/gen/typekit.rb, line 955 def smart_ptr(ptr_name, base_type, options = Hash.new) typekit = self # create a validated options-hash options = Kernel.validate_options options, # just the plain name of the header like "path/Header.hpp" :include => [], # additional pkg-config info like "pkg-config-name:path/Header.hpp" :orogen_include => [] # create opaque opaque = opaque_type("#{ptr_name}<#{base_type.name}>", base_type, options.merge(:needs_copy => false)) do |from, into| # create code from template Generation.render_template('typekit/smart_ptr.cpp', binding) end if base_type_inc = existing_orogen_include_for_type(base_type) base_type_inc.each do |or_inc| opaque.metadata.add('orogen_include', or_inc) end end # handle the "orogen_include" option, which has to contain the # pkg-config-name of the software providing this header options[:orogen_include].each do |or_inc| included_files << or_inc.split(':').last opaque.metadata.add('orogen_include', or_inc) end # Add the headers required for this smart pointer definition to # the list of included files. we dont have any pkg-config # informations in this channel, so just prepend a ":" to denote # the empty case. options[:include].each do |inc| included_files << inc # note that the "opaque_type()" factory-function used # previously to create the opaque for the ro_ptr will take # care that this header is added to the "orogen_include" # metadata as well end # ...and copy the header-informations needed for the type # verbatim. base_type.metadata.get('orogen_include').each do |or_inc| opaque.metadata.add('orogen_include', or_inc) end # Tell the port codegen that we must set a non-empty smart # pointer, or connections will fail. # # It's up to the user to set something more meaningful, but we # at least guarantee that things will work opaque.metadata.add 'orogen:cxx_port_codegen:constructor', "_%s.setDataSample(#{opaque.cxx_name}(new #{base_type.cxx_name}));" opaque end
# File lib/orogen/gen/typekit.rb, line 568 def standalone? !project end
# File lib/orogen/gen/typekit.rb, line 1934 def transport_plugin_name(transport_name) self.class.transport_plugin_name(transport_name, self.name) end
Change or read the current type export policy. This policy drives what types orogen will import in the RTT type system.
If the :all policy is used (the default), then all types that have been imported through header files will be included. This can generate a lot of code, and therefore produce high compilation times and big typekit libraries.
If the :used policy is used, then will only be exported the types that are actually used in a task context of the typekit's project. This is most of the time a good idea.
If the :selected policy is used, then only types that have been explicitely selected with export_types will be included.
# File lib/orogen/gen/typekit.rb, line 657 dsl_attribute :type_export_policy do |new_policy| new_policy = new_policy.to_sym if !TYPE_EXPORT_POLICIES.include?(new_policy) raise ArgumentError, "invalid type export policy #{new_policy.inspect}, allowed are: :#{TYPE_EXPORT_POLICIES.join(", :")}" end if new_policy == :used && !project raise ArgumentError, "cannot use a 'used' policy on a standalone typekit" end new_policy end
# File lib/orogen/gen/typekit.rb, line 2213 def type_info_includes_for_type(type) if type.opaque? return type_info_includes_for_type(intermediate_type_for(type)) elsif TypekitMarshallers::TypeInfo::Plugin.rtt_scripting? result = ["#{self.name}/typekit/BoostSerialization.hpp", type.info_type_header] if type.full_name == "/std/string" result << "rtt/typekit/StdStringTypeInfo.hpp" end result else ["rtt/types/PrimitiveTypeInfo.hpp", "rtt/types/TemplateConnFactory.hpp"] end end
Computes the set of typekits that are required to get the given types.
self is never included in the result
# File lib/orogen/gen/typekit.rb, line 1637 def typekits_required_for(types) result = Set.new types.each do |type| loop do imported_typekits.each do |tk| if type < Typelib::ArrayType if tk.defines_array_of?(type.deference) result << tk end elsif tk.includes?(type.name) result << tk end end if type.respond_to?(:deference) type = type.deference else break end end end result end
List of typekits that this typekit depends on
# File lib/orogen/gen/typekit.rb, line 1661 def used_typekits @registry = normalize_registry typekits_required_for(registry.each) end
# File lib/orogen/gen/typekit.rb, line 716 def using_library(library, options = Hash.new) if library.respond_to?(:to_str) library = Utilrb::PkgConfig.new(library) end options = Kernel.validate_options options, :link => true self.used_libraries << library self.include_dirs |= library.include_dirs.to_set if options[:link] self.linked_used_libraries << library end end
# File lib/orogen/gen/typekit.rb, line 705 def using_typekit(typekit) self.imported_types.merge(typekit.registry) self.imported_typelist |= typekit.typelist self.include_dirs |= typekit.include_dirs.to_set self.opaques.concat(typekit.opaques) if dir = typekit.types_dir self.include_dirs << dir end self.imported_typekits << typekit end
True if the orocos target is xenomai
# File lib/orogen/gen/typekit.rb, line 830 def xenomai?; orocos_target == 'xenomai' end