module Rock::Bundles
Bundle support
In Rock, bundles are the packages in which system-related scripting, modelling and tools are included. They are meant to “bind together” the different components, packages and tool configuration from rock.
In practice, the bundle implementation is a thin wrapper on top of the rest of the rock
Bundle-aware scripts should simply use Bundle.initialize / Bundle.load instead of Orocos.initialize / Orocos.load. This is in principle enough to get bundle integration. All the rock-* tools on which bundle integration makes sense should do that already.
Attributes
Public Class Methods
# File lib/rock/bundles.rb, line 502 def self.change_default_options(args, defaults) args = args.dup if args.last.kind_of?(Hash) with_defaults, other = Kernel.filter_options args.pop, defaults args.push(with_defaults.merge(other)) else args.push(defaults) end args end
The full path to the configuration directory
This returns the full path to the directory that contains the configuration files, given the current bundle setup. It creates this directory if it does not exist
@return [String] the path to the directory. It is guaranteed to exist. @raise (see ::current_bundle)
# File lib/rock/bundles.rb, line 327 def self.config_dir dir = File.join(current_bundle.path, 'config') if !File.directory?(dir) FileUtils.mkdir_p dir end dir end
Returns the Bundle object that represents the current bundle (i.e. the bundle in which we shoul be working).
The current bundle can be defined, in order of priority, by:
-
the ROCK_BUNDLE environment variable, which should hold the name of the currently selected bundle
-
the current directory
-
if there is only one bundle found on this system, this bundle is returned
# File lib/rock/bundles.rb, line 208 def self.current_bundle all_bundles = each_bundle.to_a if bundle_name = ENV['ROCK_BUNDLE'] bundle_path = File.expand_path(bundle_name) if bdl = all_bundles.find { |bdl| bdl.name == bundle_name || bdl.path == bundle_path } return bdl else raise ArgumentError, "cannot find currently selected bundle #{bundle_name} (available bundles are: #{all_bundles.map(&:name).sort.join(", ")})" end elsif current = find_bundle_from_current_dir return current elsif all_bundles.size == 1 return all_bundles.first else raise NoBundle, "no bundle found" end end
Returns an array containing both root_bundle and its
dependencies (recursively). The array is returned in order of priority.
# File lib/rock/bundles.rb, line 236 def self.discover_dependencies(root_bundle) all_bundles = self.each_bundle.to_a result = [] queue = [root_bundle] while !queue.empty? root = queue.shift result.delete(root) next if queue.include?(root) result << root root.each_dependency do |bundle_name| bdl = all_bundles.find { |b| b.name == bundle_name } if !bdl raise BundleNotFound.new(bundle_name), "could not find bundle #{bundle_name}, listed as dependency in #{root.name} (#{root.path})" end queue << bdl end end result end
Enumerates the path to every registered bundle, starting with the one that has the highest priority
A directory is considered to be a bundle if it has a config/app.yml or config/bundle.yml file (even an empty one)
The bundles are enumerated following the ROCK_BUNDLE_PATH environment variable:
-
if a directory in ROCK_BUNDLE_PATH is pointing to a bundle, it gets added
-
if a directory in ROCK_BUNDLE_PATH is a directory that contains bundles, all the included bundles are added in an unspecified order
# File lib/rock/bundles.rb, line 148 def self.each_bundle if !block_given? return enum_for(:each_bundle) end paths = (ENV['ROCK_BUNDLE_PATH'] || '').split(":") if paths.empty? if from_pwd = find_bundle_from_current_dir paths << from_pwd.path end end current_bundle = ENV['ROCK_BUNDLE'] if current_bundle && current_bundle.empty? current_bundle = nil end if current_bundle && Bundles.is_bundle_path?(current_bundle) yield(Bundle.new(File.expand_path(current_bundle))) end paths.each do |path| if !File.directory?(path) elsif is_bundle_path?(path) yield(Bundle.new(path)) else Dir.new(path).each do |f| f = File.join(path, f) if is_bundle_path?(f) yield(Bundle.new(f)) end end end end end
Find the bundle in which we are currently, if there is one
Returns nil if we are outside any bundle
# File lib/rock/bundles.rb, line 96 def self.find_bundle_from_current_dir find_bundle_from_dir(Dir.pwd) end
Find the bundle that contains the provided directory
Returns nil if there is none
# File lib/rock/bundles.rb, line 125 def self.find_bundle_from_dir(dir) # Look for a bundle in the parents of Dir.pwd bundle_dir = Pathname.new(dir). find_matching_parent { |curdir| is_bundle_path?(curdir.to_s) } if bundle_dir return Bundle.new(bundle_dir.to_s) end end
# File lib/rock/bundles.rb, line 516 def self.find_dir(*args) args = change_default_options(args, :order => :specific_first) Roby.app.find_dir(*args) end
# File lib/rock/bundles.rb, line 513 def self.find_dirs(*args) Roby.app.find_dirs(*args) end
# File lib/rock/bundles.rb, line 520 def self.find_file(*args) args = change_default_options(args, :order => :specific_first) Roby.app.find_file(*args) end
# File lib/rock/bundles.rb, line 524 def self.find_files(*args) Roby.app.find_files(*args) end
# File lib/rock/bundles.rb, line 527 def self.find_files_in_dirs(*args) Roby.app.find_files_in_dirs(*args) end
Returns the task context referred to by name. Some common
configuration is done on this task, in particular the 'default'
configuration is applied if one is defined for the task's model
# File lib/rock/bundles.rb, line 446 def self.get(task_name) task = Orocos.name_service.get(task_name) if !defined?(Orocos::Log::TaskContext) || !task.kind_of?(Orocos::Log::TaskContext) Orocos.conf.apply(task, ['default']) end task end
Returns true if we have a selected bundle
# File lib/rock/bundles.rb, line 227 def self.has_selected_bundle? current_bundle true rescue NoBundle false end
# File lib/rock/bundles.rb, line 370 def self.has_transformer? Orocos.respond_to?(:transformer) end
Initializes the bundle support, and initializes the orocos layer
# File lib/rock/bundles.rb, line 428 def self.initialize(*args) # All logs are public by default in bundle scripts. This is overriden in rock-roby Roby.app.public_logs = Bundles.public_logs? if Orocos.initialized? raise ArgumentError, "Orocos.initialize has already been called. Do not call Orocos.initialize and Bundles.initialize multiple times" end self.load if Bundles.public_logs? Bundles.info "log files are going in #{Bundles.log_dir}" end Orocos.initialize(*args) end
Returns true if path is the root of a bundle
# File lib/rock/bundles.rb, line 88 def self.is_bundle_path?(path) File.file?(File.join(path, "config", "bundle.yml")) || File.file?(File.join(path, "config", "app.yml")) end
# File lib/rock/bundles.rb, line 418 def self.is_ruby_script?(file) if file =~ /\.rb$/ return true else first_line = File.open(file).each_line.find { true } return first_line =~ /^#!.*ruby/ end end
Initializes the bundle support, and load all the available orocos info
# File lib/rock/bundles.rb, line 336 def self.load(required = false) setup_search_paths require 'orocos' dir = Bundles.log_dir while !File.directory?(dir) Bundles.log_dir_created << dir dir = File.dirname(dir) end FileUtils.mkdir_p Bundles.log_dir Orocos.default_working_directory = Bundles.log_dir ENV['ORO_LOGFILE'] = File.join(Bundles.log_dir, "orocos.orocosrb-#{::Process.pid}.txt") Orocos.load # Load configuration directories find_dirs('config', 'orogen', :order => :specific_last, :all => true).each do |dir| Orocos.conf.load_dir(dir) end # Check if the transformer is available. It if is, set it up begin require 'transformer/runtime' Transformer.use_bundle_loader @transformer_config ||= "transforms.rb" if conf_file = find_file('config', @transformer_config, :order => :specific_first) Orocos.transformer.load_conf(conf_file) end rescue LoadError end end
# File lib/rock/bundles.rb, line 454 def self.log_all(*args, &block) Orocos.log_all(*args, &block) end
# File lib/rock/bundles.rb, line 462 def self.log_all_configuration(*args, &block) Orocos.log_all_configuration(*args, &block) end
# File lib/rock/bundles.rb, line 458 def self.log_all_ports(*args, &block) Orocos.log_all_ports(*args, &block) end
# File lib/rock/bundles.rb, line 530 def self.log_dir if Roby.app.created_log_dir? Roby.app.log_dir else Roby.app.find_and_create_log_dir end end
# File lib/rock/bundles.rb, line 379 def self.method_missing(m, *args, &block) if Orocos.respond_to?(m) Orocos.send(m, *args, &block) else super end end
# File lib/rock/bundles.rb, line 470 def self.parse_script_options(argv) parser = OptionParser.new do |opt| opt.on('--gdb[=TASKS]', String, 'run the comma-separated list of deployments using gdbserver. Do all if no arguments are given.') do |gdb| Bundles::Scripts.gdb = gdb.split(',') end opt.on('--gdb-options=OPTIONS', String, 'comma-separated list of options that should be passed to the started gdbserver') do |gdb_options| Bundles::Scripts.gdb_options = gdb_options.split(',') end opt.on('--gdb-port=PORT', Integer, 'base port that should be used for gdbserver') do |gdb_port| Orocos::Process.gdb_base_port = gdb_port end opt.on('--valgrind[=TASKS]', String, 'run the comma-separated list of deployments using valgrind. Do all if no arguments are given.') do |valgrind| Bundles::Scripts.valgrind = valgrind.split(',') end opt.on('--valgrind-options=OPTIONS', String, 'comma-separated list of options that should be passed to the started valgrind') do |valgrind_options| Bundles::Scripts.valgrind_options = valgrind_options.split(',') end opt.on('--wait=SECONDS', Integer, 'wait that many seconds before deciding that a process will not start') do |wait| Bundles::Scripts.wait = wait end opt.on("-h", "--help", "this help message") do puts opt exit 0 end if block_given? yield(opt) end end parser.parse(argv) end
# File lib/rock/bundles.rb, line 544 def self.public_logs=(value) @public_logs = value end
If true, the logs are going to be kept. Otherwise, the log folder is going to be deleted on shutdown
# File lib/rock/bundles.rb, line 540 def self.public_logs? @public_logs end
# File lib/rock/bundles.rb, line 387 def self.run(*args, &block) # Process the given options to override some of the defaults options = if args.last.kind_of?(Hash) args.pop else Hash.new end cmdline_options = [:gdb, :gdb_options, :valgrind, :valgrind_options, :wait] cmdline_wrappers, options = Kernel.filter_options options, cmdline_options cmdline_options.each do |key| if value = Scripts.send(key) cmdline_wrappers[key] = value end end output_options, options = Kernel.filter_options options, :output => "%m-%p.txt" options = options.merge(cmdline_wrappers) options = options.merge(output_options) args.push(options) if has_transformer? && Transformer.broadcaster_name Orocos.transformer.start_broadcaster(Transformer.broadcaster_name, output_options) do Orocos.run(*args, &block) end else Orocos.run(*args, &block) end end
Select the bundle pointed-to by directory, or the bundle that
contains directory
Raises ArgumentError if directory is not part of a bundle
# File lib/rock/bundles.rb, line 114 def self.select(directory) if current_bundle = find_bundle_from_dir(Dir.pwd) ENV['ROCK_BUNDLE'] = current_bundle.path else raise ArgumentError, "#{directory} is not contained in a bundle" end end
Selects the current bundle, regardless of the global configuration
Raises ArgumentError if the current directory is not a bundle, or is not contained in one
This is meant to be mostly used in test scripts
# File lib/rock/bundles.rb, line 106 def self.select_current select(Dir.pwd) end
# File lib/rock/bundles.rb, line 259 def self.setup_search_paths(required = false) current_bundle = begin self.current_bundle rescue NoBundle if required raise end end if current_bundle selected_bundles = discover_dependencies(current_bundle) if @selected_bundles != selected_bundles Bundles.info "Active bundles: #{selected_bundles.map(&:name).join(", ")}" end else current_bundle = Bundle.new(Dir.pwd) selected_bundles = [current_bundle] if @selected_bundles != selected_bundles Bundles.info "No bundle currently selected" end end selected_bundles.reverse.each do |b| $LOAD_PATH.unshift(b.path) unless $LOAD_PATH.include?(b.path) end # Check if the current directory is in a bundle, and if it is the # case if that bundle is part of the selection. Otherwise, issue a # warning if current_dir = find_bundle_from_current_dir if !selected_bundles.any? { |b| b.path == current_dir.path } sel = each_bundle.find { |b| b.path == current_dir.path } Bundles.warn "" Bundles.warn "The bundle that contains the current directory," Bundles.warn " #{current_dir.name} (#{current_dir.path})" Bundles.warn "is not currently active" Bundles.warn "" if sel Bundles.warn "Did you mean to do bundles-sel #{sel.name} ?" else Bundles.warn "Did you mean to do bundles-sel #{current_dir.path} ?" end end end Roby.app.app_dir = current_bundle.path Roby.app.load_config_yaml rock_bundle_path = (ENV['ROCK_BUNDLE_PATH'] || "").split(":") Roby.app.search_path = selected_bundles.map(&:path) + rock_bundle_path selected_bundles.reverse.each do |b| libdir = File.join(b.path, "lib") if File.directory?(libdir) $LOAD_PATH.unshift libdir end end @selected_bundles = selected_bundles end
# File lib/rock/bundles.rb, line 374 def self.transformer_config=(file_name) # sets the name of the transformer file found in the config folder @transformer_config = file_name end
# File lib/rock/bundles.rb, line 466 def self.watch(*args, &block) Orocos.watch(*args, &block) end