Module: Rock::Bundles

Defined in:
lib/rock/bundles.rb

Overview

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.

Defined Under Namespace

Modules: Scripts Classes: Bundle, BundleNotFound, NoBundle

Class Attribute Summary collapse

Class Method Summary collapse

Class Attribute Details

.log_dir_createdObject (readonly)

Returns the value of attribute log_dir_created



551
552
553
# File 'lib/rock/bundles.rb', line 551

def log_dir_created
  @log_dir_created
end

Class Method Details

.change_default_options(args, defaults) ⇒ Object



502
503
504
505
506
507
508
509
510
511
# 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_dirString

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

Returns:

  • (String)

    the path to the directory. It is guaranteed to exist.



327
328
329
330
331
332
333
# 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_bundleObject

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



208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
# 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) ⇒ Object

Returns an array containing both root_bundle and its dependencies (recursively). The array is returned in order of priority.



236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
# 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_bundleObject

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



148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
# 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_dirObject

Find the bundle in which we are currently, if there is one

Returns nil if we are outside any bundle



96
97
98
# 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) ⇒ Object

Find the bundle that contains the provided directory

Returns nil if there is none



125
126
127
128
129
130
131
132
# 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) ⇒ Object



516
517
518
519
# 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) ⇒ Object



513
514
515
# File 'lib/rock/bundles.rb', line 513

def self.find_dirs(*args)
    Roby.app.find_dirs(*args)
end

.find_file(*args) ⇒ Object



520
521
522
523
# 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) ⇒ Object



524
525
526
# File 'lib/rock/bundles.rb', line 524

def self.find_files(*args)
    Roby.app.find_files(*args)
end

.find_files_in_dirs(*args) ⇒ Object



527
528
529
# 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) ⇒ Object

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



446
447
448
449
450
451
452
# 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?Boolean

Returns true if we have a selected bundle

Returns:

  • (Boolean)


227
228
229
230
231
232
# File 'lib/rock/bundles.rb', line 227

def self.has_selected_bundle?
    current_bundle
    true
rescue NoBundle
    false
end

.has_transformer?Boolean

Returns:

  • (Boolean)


370
371
372
# File 'lib/rock/bundles.rb', line 370

def self.has_transformer?
    Orocos.respond_to?(:transformer)
end

.initialize(*args) ⇒ Object

Initializes the bundle support, and initializes the orocos layer



428
429
430
431
432
433
434
435
436
437
438
439
440
441
# 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) ⇒ Boolean

Returns true if path is the root of a bundle

Returns:

  • (Boolean)


88
89
90
91
# 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) ⇒ Boolean

Returns:

  • (Boolean)


418
419
420
421
422
423
424
425
# 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) ⇒ Object

Initializes the bundle support, and load all the available orocos info



336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
# 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) ⇒ Object



454
455
456
# File 'lib/rock/bundles.rb', line 454

def self.log_all(*args, &block)
    Orocos.log_all(*args, &block)
end

.log_all_configuration(*args, &block) ⇒ Object



462
463
464
# 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) ⇒ Object



458
459
460
# File 'lib/rock/bundles.rb', line 458

def self.log_all_ports(*args, &block)
    Orocos.log_all_ports(*args, &block)
end

.log_dirObject



530
531
532
533
534
535
536
# 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) ⇒ Object



379
380
381
382
383
384
385
# 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) ⇒ Object



470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
# 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) ⇒ Object



544
545
546
# File 'lib/rock/bundles.rb', line 544

def self.public_logs=(value)
    @public_logs = value
end

.public_logs?Boolean

If true, the logs are going to be kept. Otherwise, the log folder is going to be deleted on shutdown

Returns:

  • (Boolean)


540
541
542
# File 'lib/rock/bundles.rb', line 540

def self.public_logs?
    @public_logs
end

.run(*args, &block) ⇒ Object



387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
# 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) ⇒ Object

Select the bundle pointed-to by directory, or the bundle that contains directory

Raises ArgumentError if directory is not part of a bundle



114
115
116
117
118
119
120
# 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_currentObject

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



106
107
108
# File 'lib/rock/bundles.rb', line 106

def self.select_current
    select(Dir.pwd)
end

.setup_search_paths(required = false) ⇒ Object



259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
# 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) ⇒ Object



374
375
376
377
# 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) ⇒ Object



466
467
468
# File 'lib/rock/bundles.rb', line 466

def self.watch(*args, &block)
    Orocos.watch(*args, &block)
end