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
Public Instance Methods
Source
# File lib/origen/remote_manager.rb, line 90 def create_symlink(from, to) if Origen.running_on_windows? system("call mklink /D #{to.to_s.gsub('/', '\\')} #{from.to_s.gsub('/', '\\')}") File.new("#{to}_is_a_symlink", 'w') {} else FileUtils.symlink from, to end end
Handles all symlink creation since Ruby/Windows is ropey. On windows it will create an additional flag file to easily tell us a symlink is active later. @api private
Source
# File lib/origen/remote_manager.rb, line 101 def delete_symlink(path) if Origen.running_on_windows? # Don't use regular rm on windows symlink, will delete into the remote dir! system("call cmd /c rmdir #{path.to_s.gsub('/', '\\')}") FileUtils.rm_f("#{path}_is_a_symlink") else FileUtils.rm_f(path) end end
Manually handle symlink deletion to support windows @api private
Source
# File lib/origen/remote_manager.rb, line 50 def imports_required? true # Guaranteed by the boot process end
Returns true if the imports have already been required and added to the load path of the current thread
Source
# File lib/origen/remote_manager.rb, line 78 def named_remotes remotes end
Returns a hash containing all remotes
Source
# File lib/origen/remote_manager.rb, line 73 def names named_remotes.keys end
Returns an array of symbols that represent the names of all remotes
Source
# File lib/origen/remote_manager.rb, line 83 def origen_root(name) origen_root_for(named_remotes[name]) end
Returns the path to origen root for the given import name
Source
# 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
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.
Source
# File lib/origen/remote_manager.rb, line 44 def required? @required end
Returns true if the imports have already been required and added to the load path of the current thread
Source
# File lib/origen/remote_manager.rb, line 124 def resolve_remotes! resolve_remotes process_remotes end
Fetches any defined remotes, regardless of whether they are dirty or not
Source
# File lib/origen/remote_manager.rb, line 115 def symlink?(path) if Origen.running_on_windows? File.exist?("#{path}_is_a_symlink") else File.symlink?(path) end end
Returns true if the given path is a symlink. Since Rubies handling of symlinks is ropey we will manually maintain a flag (an empty file that means true when present) to indicate when an import is currently setup as a symlink to a path. @api private
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
Source
# 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
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.
Source
# File lib/origen/remote_manager.rb, line 238 def current_version_of(remote) ws.current_version_of(workspace_of(remote)) end
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
Source
# File lib/origen/remote_manager.rb, line 173 def dir_of(remote) dir_defined?(remote) dir = remote[:dir] end
Returns the name of the given import (a lower cased symbol)
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
Source
# File lib/origen/remote_manager.rb, line 222 def dirty_remotes remotes.select do |_name, remote| dirty?(remote) end end
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
Source
# 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
Returns the name of the given import (a lower cased symbol)
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
Source
# File lib/origen/remote_manager.rb, line 243 def path_enabled?(remote) dir = workspace_of(remote) File.exist?(dir) && symlink?(dir) end
Returns true if the given import is currently setup as a path
Source
# 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
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
Source
# 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
Process each remote
Source
# File lib/origen/remote_manager.rb, line 276 def remotes @remotes ||= resolve_remotes end
Source
# 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
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.
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
Source
# 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
Makes all dirty remotes clean
Source
# File lib/origen/remote_manager.rb, line 217 def updates_required? resolve_remotes dirty_remotes.size > 0 end
Source
# File lib/origen/remote_manager.rb, line 368 def workspace_of(remote) Pathname.new("#{ws.remotes_directory}/#{dir_of(remote)}") end