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

log_dir_created[R]

Public Class Methods

change_default_options(args, defaults) click to toggle source
# 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
config_dir() click to toggle source

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
current_bundle() click to toggle source

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
discover_dependencies(root_bundle) click to toggle source

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
each_bundle() { |bundle(expand_path)| ... } click to toggle source

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_bundle_from_current_dir() click to toggle source

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_bundle_from_dir(dir) click to toggle source

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
find_dir(*args) click to toggle source
# 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
find_dirs(*args) click to toggle source
# File lib/rock/bundles.rb, line 513
def self.find_dirs(*args)
    Roby.app.find_dirs(*args)
end
find_file(*args) click to toggle source
# 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
find_files(*args) click to toggle source
# File lib/rock/bundles.rb, line 524
def self.find_files(*args)
    Roby.app.find_files(*args)
end
find_files_in_dirs(*args) click to toggle source
# File lib/rock/bundles.rb, line 527
def self.find_files_in_dirs(*args)
    Roby.app.find_files_in_dirs(*args)
end
get(task_name) click to toggle source

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
has_selected_bundle?() click to toggle source

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
has_transformer?() click to toggle source
# File lib/rock/bundles.rb, line 370
def self.has_transformer?
    Orocos.respond_to?(:transformer)
end
initialize(*args) click to toggle source

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
is_bundle_path?(path) click to toggle source

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
is_ruby_script?(file) click to toggle source
# 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
load(required = false) click to toggle source

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
log_all(*args, &block) click to toggle source
# File lib/rock/bundles.rb, line 454
def self.log_all(*args, &block)
    Orocos.log_all(*args, &block)
end
log_all_configuration(*args, &block) click to toggle source
# File lib/rock/bundles.rb, line 462
def self.log_all_configuration(*args, &block)
    Orocos.log_all_configuration(*args, &block)
end
log_all_ports(*args, &block) click to toggle source
# File lib/rock/bundles.rb, line 458
def self.log_all_ports(*args, &block)
    Orocos.log_all_ports(*args, &block)
end
log_dir() click to toggle source
# 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
method_missing(m, *args, &block) click to toggle source
Calls superclass method
# 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
parse_script_options(argv) { |opt| ... } click to toggle source
# 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
public_logs=(value) click to toggle source
# File lib/rock/bundles.rb, line 544
def self.public_logs=(value)
    @public_logs = value
end
public_logs?() click to toggle source

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
run(*args, &block) click to toggle source
# 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(directory) click to toggle source

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
select_current() click to toggle source

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
setup_search_paths(required = false) click to toggle source
# 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
transformer_config=(file_name) click to toggle source
# 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
watch(*args, &block) click to toggle source
# File lib/rock/bundles.rb, line 466
def self.watch(*args, &block)
    Orocos.watch(*args, &block)
end