module Origen # In Origen v2 this class was introduced to formally co-ordinate application level # configuration of Origen. # # == Configuration # # See Origen::Application::Configuration for the available options. class Application autoload :Configuration, 'origen/application/configuration' autoload :Target, 'origen/application/target' autoload :Environment, 'origen/application/environment' autoload :PluginsManager, 'origen/application/plugins_manager' autoload :Plugins, 'origen/application/plugins' autoload :LSF, 'origen/application/lsf' autoload :Runner, 'origen/application/runner' autoload :LSFManager, 'origen/application/lsf_manager' autoload :Release, 'origen/application/release' autoload :Deployer, 'origen/application/deployer' autoload :VersionTracker, 'origen/application/version_tracker' autoload :CommandDispatcher, 'origen/application/command_dispatcher' autoload :WorkspaceManager, 'origen/application/workspace_manager' require 'origen/users' include Origen::Users attr_accessor :name attr_accessor :namespace class << self def inherited(base) # Somehow using the old import system and version file format we can get in here when # loading the version, this can be removed in future when the imports API is retired unless caller[0] =~ /version.rb.*/ if base.to_s == 'OrigenGlobalApplication' app = base.instance app.root = Origen.root Origen.register_application(app) else root = Pathname.new(caller[0].sub(/(\\|\/)?config(\\|\/)application.rb.*/, '')).realpath app = base.instance app.root = root.to_s if Origen.plugins_loaded? && !Origen.loading_top_level? # This situation of a plugin being loaded after the top-level app could occur if the app # doesn't require the plugin until later, in that case there is nothing the plugin owner # can do and we just need to accept that this can happen. # Origen.log.warning "The #{app.name} plugin is using a non-standard loading mechanism, upgrade to a newer version of it to get rid of this warning (please report a bug to its owner if this warning persists)" Origen.register_application(app) # Origen.app.plugins << app else Origen.register_application(app) end app.add_lib_to_load_path! end end end def instance @instance ||= new end def respond_to?(*args) super || instance.respond_to?(*args) end protected def method_missing(*args, &block) instance.send(*args, &block) end end # A simple class to load all rake tasks available to an application, a class is used here # to avoid collision with the Rake namespace method class RakeLoader require 'rake' include Rake::DSL def load_tasks $VERBOSE = nil # Don't care about world writable dir warnings and the like require 'colored' # Load all Origen tasks first Dir.glob("#{Origen.top}/lib/tasks/*.rake").sort.each do |file| load file end # Now the application's own tasks if Origen.app.origen_core? Dir.glob("#{Origen.root}/lib/tasks/private/*.rake").sort.each do |file| load file end else Dir.glob("#{Origen.root}/lib/tasks/*.rake").sort.each do |file| load file end end # Finally those that the plugin's have given us ([Origen.app] + Origen.app.plugins).each do |plugin| namespace plugin.name do Dir.glob("#{plugin.root}/lib/tasks/shared/*.rake").sort.each do |file| load file end end end end end def current_job current_jobs.last end def current_jobs @current_jobs ||= [] end # Load all rake tasks defined in the application's lib/task directory def load_tasks RakeLoader.new.load_tasks end # Returns a revision controller instance (e.g. Origen::RevisionControl::Git) which has # been configured to point to the local workspace and the remote repository # as defined by Origen.app.config.rc_url. If the revision control URL has not been # defined, or it does not resolve to a recognized revision control system, then this # method will return nil. def revision_controller(options = {}) if current? if config.rc_url begin if config.rc_url =~ /^sync:/ @revision_controller ||= RevisionControl::DesignSync.new( local: root, remote: config.rc_url ) elsif config.rc_url =~ /git/ @revision_controller ||= RevisionControl::Git.new( local: root, # If a workspace is based on a fork of the master repo, config.rc_url may not # be correct remote: (options[:uninitialized] ? config.rc_url : (RevisionControl::Git.origin || config.rc_url)), allow_local_adjustment: true ) end # The rc_url has been defined, but the initial app checkin has not been done yet rescue RevisionControlUninitializedError @revision_controller = nil end elsif config.vault @revision_controller ||= RevisionControl::DesignSync.new( local: root, remote: config.vault ) end else fail "Only the top-level application has a revision controller! #{name} is a plugin" end end alias_method :rc, :revision_controller # This callback handler will fire once the main app and all of its plugins have been loaded def on_loaded config.log_deprecations end # Convenience method to check if the given application instance is Origen core def origen_core? name.to_s.symbolize == :origen_core end def inspect "" end def root=(val) @root = Pathname.new(val) end def require_environment! Origen.deprecate 'Calling app.require_environment! is no longer required, the app environment is now automtically loaded when Origen.app is called' end # Returns a full path to the root directory of the given application # # If the application instance is a plugin then this will point to where # the application is installed within the imports directory def root @root end # Returns a path to the imports directory (e.g. used by the remotes and similar features) for the # application. e.g. if the app live at /home/thao/my_app, then the imports directory will typically # be /home/thao/.my_app_imports_DO_NOT_HAND_MODIFY # # Origen will ensure that this directory is outside of the scope of the current application's revision # control system. This prevents conflicts with the revision control system for the application and those # used to import 3rd party dependencies def imports_directory workspace_manager.imports_directory end alias_method :imports_dir, :imports_directory # Returns a path to the remotes directory for the application. e.g. if the app live # at /home/thao/my_app, then the remotes directory will typically # be /home/thao/.my_app_remotes_DO_NOT_HAND_MODIFY # # Origen will ensure that this directory is outside of the scope of the current application's revision # control system. This prevents conflicts with the revision control system for the application and those # used to import 3rd party dependencies def remotes_directory workspace_manager.remotes_directory end alias_method :remotes_dir, :remotes_directory # Returns the namespace used by the application as a string def namespace @namespace ||= self.class.to_s.split('::').first.gsub('_', '').sub('Application', '') end # Returns array of email addresses in the DEV maillist file def maillist_dev maillist_parse(maillist_dev_file) end # Returns array of email addresses in the PROD maillist file def maillist_prod maillist_parse(maillist_prod_file) end # Returns default location of DEV maillist file (customize locally if needed) def maillist_dev_file Origen.app.root.to_s + '/config/maillist_dev.txt' end # Returns default location of PROD maillist file (customize locally if needed) def maillist_prod_file Origen.app.root.to_s + '/config/maillist_prod.txt' end # Parses maillist file and returns an array of email address def maillist_parse(file) maillist = [] # if file doesn't exist, just return empty array, otherwise, parse for emails if File.exist?(file) File.readlines(file).each do |line| if index = (line =~ /\#/) # line contains some kind of comment # check if there is any useful info, ignore it not unless line[0, index].strip.empty? maillist << Origen::Users::User.new(line[0, index].strip).email end else # if line is not empty, generate an email unless line.strip.empty? maillist << Origen::Users::User.new(line.strip).email end end end end maillist end # Returns an array of users who have subscribed for production release # notifications for the given application on the website def subscribers_prod if server_data @subscribers_prod ||= server_data[:subscribers_prod].map { |u| User.new(u[:core_id]) } else [] end end # Returns an array of users who have subscribed for development release # notifications for the given application on the website def subscribers_dev if server_data @subscribers_dev ||= server_data[:subscribers_dev].map { |u| User.new(u[:core_id]) } else [] end end # Returns the server data packet available for the given application, # returns nil if none is found def server_data if name == :origen @server_data ||= Origen.client.origen else @server_data ||= Origen.client.plugins.find { |p| p[:origen_name].downcase == name.to_s.downcase } end end # Returns true if the given application instance is the # current top level application def current? # If this is called before the plugins are loaded (i.e. by a plugin's application file), then # it is definitely not the top-level app if Origen.application_loaded? Origen.app == self else Origen.root == root end end alias_method :standalone?, :current? alias_method :running_standalone?, :current? # Returns true if the given application instance is # the current plugin def current_plugin? if Origen.application_loaded? Origen.app.plugins.current == self else puts <<-END current_plugin? cannot be used at this point in your code since the app is not loaded yet. If you are calling this from config/application.rb then you can only use current_plugin? within a block: # Not OK if current_plugin? config.output_directory = "#{Origen.root}/output/dir1" else config.output_directory = "#{Origen.root}/output/dir2" end # OK config.output_directory do if current_plugin? "#{Origen.root}/output/dir1" else "#{Origen.root}/output/dir2" end end END exit 1 end end # Returns the current top-level object (the DUT) def top_level @top_level ||= begin t = toplevel_listeners.first t.controller ? t.controller : t if t end end def listeners_for(*args) callback = args.shift max = args.first.is_a?(Numeric) ? args.shift : nil options = args.shift || {} options = { top_level: :first }.merge(options) listeners = callback_listeners if Origen.top_level listeners -= [Origen.top_level.model] if options[:top_level] if options[:top_level] == :last listeners = listeners + [Origen.top_level] else listeners = [Origen.top_level] + listeners end end end listeners = listeners.select { |l| l.respond_to?(callback) }.map do |l| if l.try(:is_an_origen_model?) l.respond_to_directly?(callback) ? l : l.controller else l end end if max && listeners.size > max fail "You can only define a #{callback} callback #{max > 1 ? (max.to_s + 'times') : 'once'}, however you have declared it #{listeners.size} times for instances of: #{listeners.map(&:class)}" end listeners end def version(options = {}) @version = nil if options[:refresh] return @version if @version if Origen.running_globally? @version = Origen.version else load File.join(root, 'config', 'version.rb') if defined? eval(namespace)::VERSION @version = Origen::VersionString.new(eval(namespace)::VERSION) else # The eval of the class is required here as somehow when plugins are imported under the old # imports system and with the old version file format we can end up with two copies of the # same class constant. Don't understand it, but it is fixed with the move to gems and the # namespace-based version file format. @version = Origen::VersionString.new(eval(self.class.to_s)::VERSION) end end @version end # Returns the release note for the current or given application version def release_note(version = Origen.app.version.prefixed) version = VersionString.new(version) version = version.prefixed if version.semantic? capture = false note_started = false note = [] File.readlines("#{Origen.root}/doc/history").each do |line| line = line.strip if capture if note_started if line =~ /^Tag: #{version}Tag: #{version}Tag: #{version}Tag: ([^<]*)