ROSE Compiler Framework/Plugin

Overview

edit

Starting from Version 0.9.9.83, ROSE has a new feature to support external plugins. It borrows the design and implementation of Clang Plugins.

The process to isolate the relevant source files and headers in Clang is available at:

The interface is very similar to what Clang has, with some simplification and improvements.

With this feature, you can develop your ROSE-based tools as dynamically loadable plugins. Then you can use command line options of ROSE's default translator, rose-compiler (or another ROSE translator), to

  • load shared libraries containing the plugins,
  • specify actions to be executed,
  • as well as pass command line options to each action.

Benefits

edit

The main benefit of using plugins is that you can use a single installed ROSE default translator to execute one or more arbitrary external plugins, in the order they appear on the command line.

This will significantly reduce the overhead of composing ROSE-based transformations, by reusing the costly parsing and unparsing, and freely chaining up the transformation plugins through command line options.

The deployment of plugins are much simpler also. No need to recompile/reinstall ROSE.

For example, we had to call two heavy-weight tools in two command lines:

# two separated command lines to run two ROSE-based tools, each of which has costly parsing and unparsing. 
tool_1 input.c;
tool_2 input.c;

Now, we can call the default ROSE rose-compiler and chain up two plugins (act1 and act2) instead:

# sharing one identitiTranslator's parsing/unparsing support,
# load multiple shared libraries, executing two actions in the order they show up in the command line, also pass multiple options to each of the plugins

rose-compiler -rose:plugin_lib lib.so -rose:plugin_lib lib2.so -rose:plugin_action act -rose:plugin_action act2 \
 -rose:plugin_arg_act1 op1 -rose:plugin_arg_act1 op2 -rose:plugin_arg_act2 op3 -rose:plugin_arg_act2 op4 

Command Line Interface

edit

rose-compiler --help , excerpt of the plugin section

Plugin Mode:
     -rose:plugin_lib <shared_lib_filename>
                             Specify the file path to a shared library built from plugin source files 
                             This option can repeat multiple times to load multiple libraries 
     -rose:plugin_action <act_name>
                             Specify the plugin action to be executed
                             This option can repeat multiple times to execute multiple actions 
                             in the order shown up in command line 
     -rose:plugin_arg_<act_name>  <option>
                             Specify one option to be passed to a plugin named act_name
                             This option can repeat multiple times to provide multiple options to a plugin 

Examples

  • rose-compiler -rose:plugin_lib /path/libPrintNamesPlugin.so -rose:plugin_action print-names -rose:plugin_arg_print-names pretty-printing
    • load a shared library containing a single plugin, execute the plugin named print-names, also pass an option named "pretty-printing" to the plugin.
  • rose-compiler -rose:plugin_lib lib.so -rose:plugin_lib lib2.so -rose:plugin_action act1 -rose:plugin_action act2 -rose:plugin_arg_act1 op1 -rose:plugin_arg_act1 op2 -rose:plugin_arg_act2 op3 -rose:plugin_arg_act2 op4
    • load multiple shared libraries, executing two actions in the order they show up in the command line, also pass multiple options to each of the plugins

Plugin Super Class

edit

Two interface functions are provided for a ROSE plugin:

  • ParseArgs(): optionally handle command line options passed to this plugin
  • process(): process the AST
  class PluginAction {
    public:
      virtual void process(SgProject*) {};
      virtual bool ParseArgs(const std::vector<std::string> &arg) {return true; };
  };

An Example Plugin

edit

You can find the full example from

This plugin will just print all defining functions' names of an input source file.

// An example ROSE plugin: PrintNamesPlugin.cpp

//Mandatory include headers
#include "rose.h"
#include "plugin.h"

// optional headers
#include "RoseAst.h" // using AST Iterator
#include <iostream>

using namespace std;
using namespace Rose;

//Step 1. Derive a plugin action from Rose::PluginAction 
class PrintNamesAction : public Rose::PluginAction {
 public:
    PrintNamesAction() {}
    ~PrintNamesAction() {}

   // This is optional. Need only if your plugin wants to handle options
  // Provide command line option processing: arg will be the options passed to this plugin
   bool ParseArgs(const std::vector<std::string> &arg)
   {
     cout<<arg.size()<< " arguments "<<endl;
     for (size_t i=0; i< arg.size(); i++)
     {
       cout<<arg[i]<<endl;
     }
     return true;
   }

    // This is mandatory: providing work in your plugin
    // Do actual work after ParseArgs();
    void process (SgProject* n) {
      SgNode* node= n;
      RoseAst ast(node);

      for(RoseAst::iterator i=ast.begin();i!=ast.end();++i) {
        SgFunctionDeclaration* fdecl= isSgFunctionDeclaration(*i);
        if (fdecl && (fdecl->get_definingDeclaration()==fdecl))
          cout<<fdecl->get_name()<<endl;
      }

    } // end process()
};

//Step 2: Declare a plugin entry with a unique name 
//        Register it under a unique action name plus some description 
static Rose::PluginRegistry::Add<PrintNamesAction>  uniquePluginName1("print-names", "print function names");

Compile the plugin

edit

A sample makefile

  • Note: copy&paste may not work since tabs will be wrongfully pasted as whitespaces. You need to restore tabs before commands in the Makefile.
# specify where the installed copy of ROSE is located. 
# Essentially the --prefix path used with configure
ROSE_INSTALL=/path/to/rose/install


## Your Plugin source files
Plugin=PrintNamesPlugin
Plugin_SOURCE=$(Plugin).cpp

## Input testcode for your plugin
TESTCODE=test1.cpp

# Standard C++ compiler stuff (see rose-config --help)
comma   := ,
CXX      = $(shell $(ROSE_INSTALL)/bin/rose-config cxx)
CPPFLAGS = $(shell $(ROSE_INSTALL)/bin/rose-config cppflags) -I.
CXXFLAGS = $(shell $(ROSE_INSTALL)/bin/rose-config cxxflags)
LIBDIRS  = $(shell $(ROSE_INSTALL)/bin/rose-config libdirs)
LDFLAGS  = $(shell $(ROSE_INSTALL)/bin/rose-config ldflags) -L. \
           $(addprefix -Wl$(comma)-rpath -Wl$(comma), $(subst :, , $(LIBDIRS)))

#-------------------------------------------------------------
# Makefile Targets
#-------------------------------------------------------------

all: $(Plugin).so

# compile the plugin and generate a shared library
# -g is recommended to be used by default to enable debugging your code
$(Plugin).so: $(Plugin_SOURCE)
        $(CXX) -g $(Plugin_SOURCE) -fpic -shared $(CPPFLAGS) $(LDFLAGS) -o $@

# test the plugin
check: $(Plugin).so
        $(ROSE_INSTALL)/bin/rose-compiler -c -rose:plugin_lib $(Plugin).so -rose:plugin_action print-names -rose:plugin_arg_print-names op1 -I. -I$(ROSE_INSTALL)/include $(TESTCODE) 

clean:
        rm -rf $(Plugin).so *.o rose_* *.dot

Running the plugin

edit

Command line and options:

  • rose-compiler -rose:plugin_lib PrintNamesPlugin.so -rose:plugin_action print-names -rose:plugin_arg_print-names op1 -c input_testPlugins.C

Sample input file: input_testPlugins.C

int foo() {}
int bar(); 
int a, b,c;

Sample output:

1 arguments
op1
"foo"