class Origen::RemoteManager

Responsible for ensuring that all dependencies defined in config.remotes are available.

Workspaces will automatically be created and updated to the correct version as required.

An instance of this class is hooked up to:

Origen.remote_manager

Public Class Methods

new() click to toggle source
# File lib/origen/remote_manager.rb, line 11
def initialize
  @required = false
end

Public Instance Methods

imports_required?() click to toggle source

Returns true if the imports have already been required and added to the load path of the current thread

# File lib/origen/remote_manager.rb, line 50
def imports_required?
  true # Guaranteed by the boot process
end
named_remotes() click to toggle source

Returns a hash containing all remotes

# File lib/origen/remote_manager.rb, line 78
def named_remotes
  remotes
end
names() click to toggle source

Returns an array of symbols that represent the names of all remotes

# File lib/origen/remote_manager.rb, line 73
def names
  named_remotes.keys
end
origen_root(name) click to toggle source

Returns the path to origen root for the given import name

# File lib/origen/remote_manager.rb, line 83
def origen_root(name)
  origen_root_for(named_remotes[name])
end
require!() click to toggle source

This will fetch all remotes (if required) It does not add the libs to the load path, and require the environments

since remotes are not assumed to be Ruby.
# File lib/origen/remote_manager.rb, line 18
def require!
  unless required?
    while updates_required?
      Origen.log.info '*******************************************************************************'
      Origen.log.info 'The following remotes need to be updated, this will now happen automatically:'
      dirty_remotes.each do |name, remote|
        if remote[:path]
          Origen.log.info "  #{name} - #{remote[:path]} (included by #{remote[:importer].name})".green
        else
          Origen.log.info "  #{name} - #{remote[:version]} (included by #{remote[:importer].name})".green
        end
      end
      Origen.log.info ''

      update!

      Origen.log.info ''
      Origen.log.info 'Finished updating remotes.'
      Origen.log.info '*******************************************************************************'
    end
    @required = true
  end
end
required?() click to toggle source

Returns true if the imports have already been required and added to the load path of the current thread

# File lib/origen/remote_manager.rb, line 44
def required?
  @required
end
resolve_remotes!() click to toggle source

Fetches any defined remotes, regardless of whether they are dirty or not

# File lib/origen/remote_manager.rb, line 124
def resolve_remotes!
  resolve_remotes
  process_remotes
end
validate_production_status(force = false) click to toggle source
# File lib/origen/remote_manager.rb, line 54
def validate_production_status(force = false)
  if Origen.mode.production? || force
    remotes.each do |_name, remote|
      if remote[:path]
        fail "The following remote is defined as a path, but that is not allowed in production: #{remote}"
      end

      version = Origen::VersionString.new(remote[:version])
      unless version.valid?
        fail "The following remote version is not in a valid format: #{remote}"
      end
      if version.latest?
        fail "Latest is not allowed as a remote version in production: #{remote}"
      end
    end
  end
end

Private Instance Methods

add_remote(new) click to toggle source

Conflicts are resolved by the following rules:

* A path reference always wins.
* If two different paths are found an errors will be raised.
* If multiple versions of the same remote are found the most
  recent one wins.
# File lib/origen/remote_manager.rb, line 285
def add_remote(new)
  # Cannot have both a tag and a version defined for a remote
  if ([:tag, :version] - new.keys).empty?
    Origen.log.error('Cannot define both a tag and a version for a remote!')
    fail
  end
  name = name_of(new)
  # If the current remote has been imported by one of it's dev dependencies
  # then always use the local workspace
  if name == @current_app_name
    new = @current_app
  end
  existing = remotes[name]
  if existing
    if new[:path]
      if existing[:path]
        if existing[:path] != new[:path]
          Origen.log.error 'Cannot resolve remote dependencies due to conflicting paths:'
          Origen.log.error "  #{name}:"
          Origen.log.error "    - #{existing[:path]}"
          Origen.log.error "    - #{new[:path]}"
          Origen.log.error ''
          fail 'Remote error!'
        end
      else
        remotes[name] = new
      end
    elsif existing[:version] != new[:version]
      existing_version = Origen::VersionString.new(existing[:version])
      if existing_version.less_than?(new[:version])
        remotes[name] = new
      end
    end
  else
    remotes[name] = new
  end
end
current_version_of(remote) click to toggle source
# File lib/origen/remote_manager.rb, line 238
def current_version_of(remote)
  ws.current_version_of(workspace_of(remote))
end
dir_defined?(remote) click to toggle source
# File lib/origen/remote_manager.rb, line 178
def dir_defined?(remote)
  if remote[:dir].nil?
    Origen.log.error 'A problem was encountered with a configuration remote'
    Origen.log.error "Error: ':dir' must be defined for each remote."
    fail 'Remote error!'
  end
  true
end
dir_of(remote) click to toggle source

Returns the name of the given import (a lower cased symbol)

# File lib/origen/remote_manager.rb, line 173
def dir_of(remote)
  dir_defined?(remote)
  dir = remote[:dir]
end
dirty?(remote) click to toggle source
# File lib/origen/remote_manager.rb, line 228
def dirty?(remote)
  if remote[:path] && path_enabled?(remote)
    false
  else
    (!remote[:path] && path_enabled?(remote)) ||
      (remote[:path] && !path_enabled?(remote)) ||
      current_version_of(remote) != Origen::VersionString.new(remote[:version])
  end
end
dirty_remotes() click to toggle source
# File lib/origen/remote_manager.rb, line 222
def dirty_remotes
  remotes.select do |_name, remote|
    dirty?(remote)
  end
end
ensure_remotes_directory() click to toggle source
# File lib/origen/remote_manager.rb, line 372
def ensure_remotes_directory
  unless remotes.empty?
    unless File.exist?(Origen.app.remotes_dir)
      FileUtils.mkdir_p Origen.app.remotes_dir
    end
  end
end
name_of(remote) click to toggle source

Returns the name of the given import (a lower cased symbol)

# File lib/origen/remote_manager.rb, line 165
def name_of(remote)
  dir_defined?(remote)
  dir = remote[:dir].dup
  dir.gsub! '/', '_'
  name = dir.downcase.to_sym
end
origen_root_for(remote, options = {}) click to toggle source
# File lib/origen/remote_manager.rb, line 187
def origen_root_for(remote, options = {})
  workspace = Pathname.new(workspace_of(remote))
  if File.exist?("#{workspace}/config/application.rb")
    root = workspace
  elsif remote[:app_path] && File.exist?("#{workspace}/#{remote[:app_path]}/config/application.rb")
    root = workspace.join(remote[:app_path])
  else
    root = workspace.join('tool_data', 'origen')
  end
  if File.exist?("#{root}/config/application.rb")
    root
  else
    if options[:accept_missing]
      nil
    else
      Origen.log.error 'A problem was encountered with the following remote:'
      Origen.log.error remote
      Origen.log.error 'Please check that all vault, version or path references are correct.'
      Origen.log.error ''
      Origen.log.error 'If you are sure that the remote is setup correctly and this error'
      Origen.log.error 'persists, you can try running the following command to blow away'
      Origen.log.error 'the local remote cache and then try again from scratch:'
      Origen.log.error ''
      Origen.log.error "rm -fr #{ws.remotes_directory}"
      Origen.log.error ''
      fail 'Remote error!'
    end
  end
end
path_enabled?(remote) click to toggle source

Returns true if the given import is currently setup as a path

# File lib/origen/remote_manager.rb, line 243
def path_enabled?(remote)
  dir = workspace_of(remote)
  File.exist?(dir) && symlink?(dir)
end
prefix_tag(tag, remote = {}) click to toggle source

If the supplied tag looks like a semantic version number, then make sure it has the ā€˜v’ prefix unless the remote has explicitly disallowed the prefix

# File lib/origen/remote_manager.rb, line 386
def prefix_tag(tag, remote = {})
  remote = {
    disable_tag_prefix: false
  }.merge(remote)

  tag = Origen::VersionString.new(tag)
  if tag.semantic? && !remote[:disable_tag_prefix]
    tag.prefixed
  else
    tag
  end
end
process_remotes() click to toggle source

Process each remote

# File lib/origen/remote_manager.rb, line 132
def process_remotes
  remotes.each do |_name, remote|
    dir = workspace_of(remote)
    rc_url = remote[:rc_url] || remote[:vault]
    tag = remote[:tag].nil? ? Origen::VersionString.new(remote[:version]) : Origen::VersionString.new(remote[:tag])
    version_file = dir.to_s + '/.current_version'
    begin
      if File.exist?("#{dir}/.initial_populate_successful")
        FileUtils.rm_f(version_file) if File.exist?(version_file)
        rc = RevisionControl.new remote: rc_url, local: dir
        rc.send rc.remotes_method, version: prefix_tag(tag, remote), force: true
        File.open(version_file, 'w') do |f|
          f.write tag
        end
      else
        rc = RevisionControl.new remote: rc_url, local: dir
        rc.send rc.remotes_method, version: prefix_tag(tag, remote), force: true
        FileUtils.touch "#{dir}/.initial_populate_successful"
        File.open(version_file, 'w') do |f|
          f.write tag
        end
      end
    rescue Origen::GitError, Origen::DesignSyncError, Origen::PerforceError => e
      # If Git failed in the remote, its usually easy to see what the problem is, but now *where* it is.
      # This will prepend the failing remote along with the error from the revision control system,
      # then rethrow the error
      e.message.prepend "When updating remotes for #{remote[:importer].name}: "
      raise e
    end
  end
end
remotes() click to toggle source
# File lib/origen/remote_manager.rb, line 276
def remotes
  @remotes ||= resolve_remotes
end
resolve_remotes() click to toggle source

Populate an array of required remotes from the current application state and resolve any duplications or conflicts. Conflicts are resolved by the following rules:

* A path reference always wins.
* If two different paths are found an errors will be raised.
* If multiple versions of the same remote are found the most
  recent one wins.
# File lib/origen/remote_manager.rb, line 255
def resolve_remotes
  @remotes = {}
  top_level_remotes
  top_level_remotes.each do |remote|
    remote[:importer] = Origen.app
    add_remote(remote)
  end
  # Add remotes from imports
  Origen.app.plugins.each do |plugin|
    plugin.config.remotes.each do |import_remote|
      import_remote[:importer] = plugin
      add_remote(import_remote) unless import_remote[:development]
    end
  end
  @remotes
end
top_level_remotes() click to toggle source
# File lib/origen/remote_manager.rb, line 272
def top_level_remotes
  Origen.app.config.remotes #+ Origen.app.config.remotes_dev (there are no core remotes at this time)
end
update!() click to toggle source

Makes all dirty remotes clean

# File lib/origen/remote_manager.rb, line 324
def update!
  ensure_remotes_directory
  dirty_remotes.each do |_name, remote|
    dir = workspace_of(remote)
    if remote[:path] || path_enabled?(remote)
      if symlink?(dir)
        delete_symlink(dir)
      else
        FileUtils.rm_rf(dir) if File.exist?(dir)
      end
    end
    if remote[:path]
      create_symlink(remote[:path], dir)
    else
      rc_url = remote[:rc_url] || remote[:vault]
      tag = remote[:tag].nil? ? Origen::VersionString.new(remote[:version]) : Origen::VersionString.new(remote[:tag])
      version_file = dir.to_s + '/.current_version'
      begin
        if File.exist?("#{dir}/.initial_populate_successful")
          FileUtils.rm_f(version_file) if File.exist?(version_file)
          rc = RevisionControl.new remote: rc_url, local: dir
          rc.send rc.remotes_method, version: prefix_tag(tag, remote), force: true
          File.open(version_file, 'w') do |f|
            f.write tag
          end
        else
          rc = RevisionControl.new remote: rc_url, local: dir
          rc.send rc.remotes_method, version: prefix_tag(tag, remote), force: true
          FileUtils.touch "#{dir}/.initial_populate_successful"
          File.open(version_file, 'w') do |f|
            f.write tag
          end
        end
      rescue Origen::GitError, Origen::DesignSyncError, Origen::PerforceError => e
        # If Git failed in the remote, its usually easy to see what the problem is, but now *where* it is.
        # This will prepend the failing remote along with the error from the revision control system,
        # then rethrow the error
        e.message.prepend "When updating remotes for #{remote[:importer].name}: "
        raise e
      end
    end
  end
end
updates_required?() click to toggle source
# File lib/origen/remote_manager.rb, line 217
def updates_required?
  resolve_remotes
  dirty_remotes.size > 0
end
workspace_of(remote) click to toggle source
# File lib/origen/remote_manager.rb, line 368
def workspace_of(remote)
  Pathname.new("#{ws.remotes_directory}/#{dir_of(remote)}")
end
ws() click to toggle source
# File lib/origen/remote_manager.rb, line 380
def ws
  Origen.app.workspace_manager
end