Plugins
Creating a Plugin
Origen plugins are essentially Origen applications themselves. Follow the standard instructions to Create a New Application and choose one of the available plugin templates rather than an application template.
For the purposes of this guide let’s say we are creating a plugin called ATDTest
and this would have a top-level lib file called lib/atd_test.rb
.
When a plugin is run from its own workspace, we would call this running in standalone or development
mode, then it is loaded like any other Origen application.
The first thing Origen does in this case is to load the plugin’s gem bundle, defined in its Gemfile
,
and then boot the plugin by loading config/boot.rb
. Normally such a file will look like this:
# config/boot.rb
require 'atd_test'
So by default this simply loads the plugin’s top-level file which in this example is
lib/atd_test.rb
.
However when the plugin is loaded as part of a parent application, neither the plugin’s Gemfile
or
its config/boot.rb
file will be loaded.
Instead the parent application’s Gemfile
will be loaded, and this will contain
a reference to our plugin. The tool that evaluates the Gemfile, called Bundler, will
inspect our plugin’s gemspec
file (in this example atd_test.gemspec
) and
it will ensure that any dependencies listed there are met. Once the gem bundle has been created
it will then load our plugin’s top-level file, lib/atd_test.rb
.
The take away from this is that the following rules should be observed when creating plugins:
- Load all of your plugin’s code from it’s top-level file (
lib/atd_test.rb
), if that requires all of the lib code you wish to share with your users then they will not need to do anything except add your plugin to theirGemfile
. - The plugin’s
config/boot.rb
file should only be used for loading any code which is for testing only or for otherwise running your plugin within its own workspace environment. - The plugin’s
Gemfile
should not be used. This should contain a line that callsgemspec
which tells Bundler to refer to the plugin’s gemspec file instead, e.g.atd_test.gemspec
. This prevents you from having to maintain the plugin’s gem dependencies in two separate locations. - The plugin’s gemspec should be used to declare all of its gem dependencies as shown below. Note that plugins must never specify an absolute version of gems in its runtime dependencies.
# atd_test.gemspec
# Declare any dependencies that the plugin will need when it is running within a parent application.
# Never specify an absolute version since that can easily conflict with other plugins if they did the
# same. Instead specify a range, normally just a minimum version that you need.
spec.add_runtime_dependency "origen_jtag", ">= 1.1.0"
# Declare any dependencies that the plugin need when running in standalone/development mode only, a
# very common example is the documentation helpers plugin. This is only required to build the plugins
# web pages, but it is not required at runtime.
# Note that absolute version numbers are allowed here since this does not apply outside of this plugin.
spec.add_development_dependency "origen_doc_helpers"
The plugin’s lib
directory will be automatically added to the Ruby load path
no special consideration is required to expose lib code to the parent application.
In our example the parent application will be able to load
any of our code like this:
require 'atd_test/atd_16b'
However as noted above the plugin’s top-level file, lib/atd_test.rb
will be automatically
loaded, therefore it is the usual convention to have this file require everything else so that the parent
application does not need to manually require anything.
To share source files, templates and commands the plugin must define config.shared
in
its config/application.rb
file, here is an example that will share everything:
config.shared = {
:patterns => "pattern",
:templates => "templates",
:programs => "program",
:command_launcher => "config/shared_commands.rb"
}
Access can be restricted by specifying a sub-directory, or by simply removing the given resource:
# Only share a subset of patterns (those that live within the pattern/shared directory, those
# in pattern/development for example would not be available to the host application)
config.shared = {
:patterns => "pattern/shared",
# :templates => "templates",
# :programs => "program",
# :command_launcher => "config/shared_commands.rb"
# :global_launcher => "config/global_commands.rb"
}
The host application can reference a template from an added
plugin by using the plugin’s name as a pointer to the
shared template directory (the directory specified in the plugin’s
config.shared[:templates]
attribute),
and then a relative path from there to the required template.
For example the doc_helpers
plugin contains a test flow layout template at
templates/shared/test/_flow.md.erb
, and an importing
application would reference this template via the path
doc_helpers/test/_flow.md.erb
.
When test pattern and program directories are shared Origen will look within these for the requests patterns/flows whenever the given plugin is designated as the current plugin.
See the Current Plugin for more detail on this.
For example say our plugin is called atd_test
and it provides a pattern
called atd_ramp.rb
, then
it could be generated like this from the parent application workspace:
origen pl atd_test
origen g atd_ramp
Alternatively you can override the current plugin at runtime like this, this is equivalent to the above:
origen g atd_ramp --plugin atd_test
Program generation source files work from exactly the same principles.
Note that since there is currently no easy way to view a list of all patterns provided by a plugin, it is conventional for the plugin to provide custom commands to generate a complete set of patterns, e.g. something like this:
origen atd_test:prod_pats # Generate all production patterns from the ATD plugin
origen atd_test:prod_prog # Generate the production test program from the ATD plugin
To share custom commands supply a path to a command definition/launcher file within the plugin as shown in the example above.
This file works very similar to the way that custom commands are added to an application.
Here is an example command launcher file from a real plugin, this adds a custom
command, origen atd_test:j750
, to the host application and also extends the
origen g
command by adding an additional option to it.
Note the use of Origen.root! and Origen.app! to refer to the plugin’s own root and application instance rather than that of the host application.
Also note that the command has been called atd_test:j750
instead of just
j750
, this is to ensure that there is no naming collision with commands
that could be added by other plugins.
In future Origen will automatically apply a plugin specific prefix to the commands like this,
but until then all plugin developers are expected to be good citizens and add this manually.
# config/shared_commands.rb
# The requested command is passed in here as @command
case @command
when "atd_test:j750"
require "#{Origen.root!}/lib/commands/j750"
# Important to exit when a command has been fulfilled or else Origen core will try and execute it
exit 0
# Add an additional option to the standard generate command, important to have no exit here
when "generate"
@application_options << ["--md5", "Apply an MD5 checksum to pattern names"]
Origen.app!.md5 = true if ARGV.include?("--md5")
# Always leave an else clause to allow control to fall back through to the Origen command handler.
# You probably want to also add the command details to the help shown via 'origen -h',
# you can do this bb adding the required text to @plugin_commands before handing control back to
# Origen.
else
@plugin_commands << <<-EOT
atd_test:j750 Generate the ATD test program for J750
EOT
end
The above allows you to share commands when running from within an application instance. But what if we want to share commands regardless of whether we are running in an application? In this way, Origen can be even further utilized as a tool distribution platform and but allow all of our non-application specific functionality to still be used. E.g., LSF manager, authentication, revision control APIs, etc.
Adding shared global commands is very similiar to adding shared application commands, as shown above. The
global_launcher
option in the config.shared
definition points to a file containing
the global commands. This file, call it config/global_commands.rb
, for example, is analogous to
the config/shared_commands.rb
file from the above and looks about the same, with the exception
that the @plugin_commands
variable is now @global_commands
. An example is below:
case @command
# Print the current version notes of the atd_test module.
when "atd_test:version_notes"
require "#{Origen.root!}/lib/commands/version_notes"
exit 0
else
@global_commands << <<-EOT
atd_test:version_notes Checks the dPDM BOM versus the DS HREFs
-h for a full description
EOT
end
Shared
commands,
but outside of an application, the plugin must be brought in as a dependency either through User Installs
or Tool Repo Installs
, or must be added to the system gems. For a refresher on what this means,
please see the Invocation Considerations Guide. For even more
details please see the Advanced Topic On Invocations.
Global
commands behave slightly differently when run from an application
versus running without one. Origen does provide a 'standalone' application which gives all the functionality,
but this application cannot be customized. If you find yourself requiring application-specific behavior, your command
is better suited to be a Shared
command.
Global
and Shared
commands may have differences
in the application behavior, they do not have any differences, from an Origen perspective, when invoked
either by a Tool Repo Install
or User Install
setup versus a System Level
setup. Bundler makes it easier to develop Global commands than using Gem natively, so it is recommended
that you use a User Install
when developing Global commands. You can read up on this is and
how to set it up in the Advanced Topic On Invocations.
Due to the way that Origen acknowledges shared, global, and application commands, a pitfall exists where user-defined commands can step on each other (known as command clobbering). This has the implication that you may not be running the command you think you are running.
Consider a plugin, called my_plugin_1
that adds and implements a shared command my_command
. This
command will be available to any application which contains my_plugin_1
as a dependency. Now, consider a second plugin,
my_plugin_2
which is also a dependency of the application. my_plugin_2
also adds and implements the
command my_command
. Both plugins are adding the same command and are clobbering the command listing. The behavior
will be dependent on the load order and can be difficult to predict.
For example, if my_plugin_1
is listed before my_plugin_2
in the application’s Gemfile
, then
my_plugin_1
will be loaded and evaluated first and my_command
in my_plugin_1
will be run. However,
that is not all the consideration needed. If my_plugin_1
actually lists my_plugin_2
as its dependency,
then my_plugin_2
will be loaded before my_plugin_1
regardless of the load order in the application’s
Gemfile
. Now, my_command
in my_plugin_2
will be run. This means that it can be difficult
to track which one of the commands will be run in the case of command clobbering between plugins. You can use the bundle
command to see what the final load order is and track down which version of the command will be run.
The solution to this is to add namespacing to each of the plugins, as shown in the Sharing Application Commands section above. However, this is up to the plugin developers to abide by. Origen does not guarantee nor enforce this behavior and is none the wiser to plugins stepping on each other. No warnings or errors are generated in the event of command clobbering.
Regarding global commands, the same pitfall exists. However, an additional issue is that a shared command can now clobber a global
command. In the current Origen implementation, shared commands take precedence over global commands. Meaning, if my_plugin_1
implements my_command
as a global command and, for one reason or another, it implements my_command
as a shared
command, then when running in an application the shared
version of my_command
will be used. However, external
to an application, the global
version of my_command
will be run. Like shared command clobbering, no warnings
or errors are generated, Origen is completely oblivious to this behavior, and it is up to the plugin developers to ensure this does not
occur.
This is a known issue which has been registerd with the core team. For tracking this issue’s status, please see the Cascading Commands Issue on Github.