Help With Custom Language Plugin & External BuildServer (Compiling)

I've been putting together a custom language plugin for doing Salesforce development in IntelliJ IDEA.  I've been making quite a bit of progress.  I got the internal compiler/builder to work.  But I'm stuck on how to get the external builder (compiler) to work.  Let me explain my plugin project structure and configuration.

I've got a project with three modules:

salesforce-client               Java module with generic classes for connecting to Salesforce
salesforce-jps-plugin          Java module with my BuilderServer classes (depends upon salesforce-client module)
salesforce-plugin               Plugin module (depends upon salesforce-client module)

I have a run/debug configuration for the plugin which is set to use the classpath of the salesforce-plugin module.  I can successfully launch and debug the plugin.

In my plugin.xml, I have the following configured for the (internal) compiler:

        <compiler implementation="au.com.borner.force.com.compiler.SalesForceCompiler"/>

So if I run the project, and go into the the Project Settings under Compiler and untick "Use External Build", and rebuild the Project, then the above SalesForceCompiler class gets invoked successfully.  Cool.

Now onto the external build setup...... I've been following this page: http://confluence.jetbrains.com/display/IDEADEV/External+Builder+API+and+Plugins and looking at the Groovy plugin setup.

In the salesforce-jps-plugin module, I have the following:

/META-INF/services/org.jetbrains.jps.incremental.BuilderService          (contains my BuilderService class name: au.com.borner.salesforce.compiler.SalesForceBuilderService)

My SalesForceBuilderService has the following:

public class SalesForceBuilderService extends BuilderService {

    @NotNull
    @Override
    public List<? extends ModuleLevelBuilder> createModuleLevelBuilders() {
        return Arrays.asList(new SalesForceModuleLevelBuilder());
    }
}


And my SalesForceModuleLevelBuilder has:

public class SalesForceModuleLevelBuilder extends ModuleLevelBuilder {

    private Logger logger = Logger.getInstance(getClass());

    public SalesForceModuleLevelBuilder() {
        super(BuilderCategory.TRANSLATOR);
    }


    @Override
    public ExitCode build(CompileContext context,
                          ModuleChunk chunk,
                          DirtyFilesHolder<JavaSourceRootDescriptor, ModuleBuildTarget> dirtyFilesHolder,
                          OutputConsumer outputConsumer) throws ProjectBuildException, IOException {


        logger.warn("Testing 1 2 3");
        context.processMessage(new CompilerMessage(getPresentableName(), BuildMessage.Kind.WARNING, "Starting Salesforce Compile..."));
        return ExitCode.OK;
    }


    @Override
    public List<String> getCompilableFileExtensions() {
        return Arrays.asList("class", "page", "trigger", "component");
    }


    @NotNull
    @Override
    public String getPresentableName() {
        return "Salesforce Compiler";
    }
}


And in my plugin.xml I have:

        <compileServer.plugin classpath="salesforce-jps-plugin.jar"/>

So, if I run the project again and go back into the Project Settings under Compiler and tick "Use External Build", and rebuild the Project, then my SalesForceModuleLevelBuilder doesn't get invoked.
I don't see the "Testing 1 2 3" or "Starting Salesforce Compile..." messages in either the console output or the build log (~/Library/Caches/IntelliJIdea12/plugins-sandbox/system/log/build-log/build.log)

From what I read in com.intellij.compiler.server.CompileServerPlugin - it looks like it should be picking up my salesforce-jps-plugin module's classpath:


  /**
   * <p>Specifies semicolon-separated list of paths which should be added to the classpath of the compile server.
   * The paths are relative to the plugin 'lib' directory.</p>
   *
   * <p>In the development mode the name of each file without extension is treated as a module name and the output directory of the module
   * is added to the classpath. If such file doesn't exists the jar is searched under 'lib' directory of the plugin sources home directory.
</p>
   */
  @Attribute("classpath")
  public String getClasspath() {
    return myClasspath;
  }


Can you tell me what I'm missing to get this working?


I have even tried packaging up my plugin and trying it out in a fresh Community Edition install.

The install looks like:
/Applications/IntelliJ IDEA 12 CE.app/plugins/salesforce/lib
     salesforce-client.jar
     salesforce-jps-plugin.jar
     salesforce-plugin.jar

But the external SalesForceModuleLevelBuilder still doesn't seem to be invoked.

8 comments
Comment actions Permalink

Hi Mark,
Your description sounds like you do everything right. If the plugin is not started, this must be definitely a classpath or/and packaging issue.
First things to check:
1. all your files located in META_INF/services are copied (and then packaged) to compilation output directory. If this is not the case, please check your resource compiler configuration.
2. Missing module dependencies may cause incomplete classpath  in development mode. salesforce-jps-plugin module is an essential part of your plugin, so "main" module of your plugin should depend on this module too.
3. In idea-home/bin/log.xml  you can enable the "#com.intellij.execution.configurations.GeneralCommandLine" log category to see the command line used to start external build process:
  <category name="#com.intellij.execution.configurations.GeneralCommandLine">
    <priority value="DEBUG"/>
  </category>

4. If your plugin is intended for IDEA 13 and later versions, there is no need to support older deprecated compiler API, as it is going to be removed completely soon.

Hope this helps and if you have any additional questions please do not hesitate to ask.

Best regards,
Eugene.

0
Comment actions Permalink

Thanks for the reply.  I've been working on PSI support for APEX recently.  But I will get back to this shortly.

0
Comment actions Permalink

Thanks Eugene!  The piece I was missing was the dependency between salesforce-jpg-plugin and the main plugin module salesforce-plugin.  It's all working now!

0
Comment actions Permalink

Hi,
I am struggling with a similar problem using Idea 14.0.3. I've added to my main plugin module a dependency on its jps-plugin module. In the plugin.xml I have:

    <compileServer.plugin classpath="morfa-jps-plugin.jar" />

(where morfa-jps-plugin.jar is the name of the jar produced by the jps-module) and I've checked that the jar file is present in ~/.IdeaC14/system/plugins-sandbox/plugins/MorfaPlugin/lib. I've also tried classpath="jps-plugin" just to be on a safe side.

Still it seems that my builderService is not picked up. I've inserted breakpoints in createModuleLevelBuilders() in my BuilderService implementation and in the constructor of my ModuleLevelBuilder implementation and none of them gets hit when I debug the plugin. Instead, when I try to build anything with the plugin I am getting error messages about SDK not being specified.

Any ideas on when I should set my breakpoints to debug the issue?

Artur

0
Comment actions Permalink

What exactly is your plugin trying to compile? A ModuleLevelBuilder extends the compilation of a Java module, and a Java module can't be compiled if the JDK is not specified.

0
Comment actions Permalink

The plugin is for a programming language Morfa that has nothing to do with Java.

The idea is to run an extenal compiler to compile each Morfa source file. I am trying to implement this along the lines of the jetbrains' Haskell plugin (https://github.com/Atsky/haskell-idea-plugin). As I understand, the Haskell plugin implements its own ModuleLevelBuilder that calls the external command 'cabal' here: https://github.com/Atsky/haskell-idea-plugin/blob/master/jps-plugin/src/org/jetbrains/jps/cabal/CabalBuilder.java

0
Comment actions Permalink

I don't know why the Haskell plugin is implemented the way it is, but the intended way to implement compilation of modules that have nothing to do with Java is to implement TargetBuilder, not ModuleLevelBuilder.

0
Comment actions Permalink

Thanks. I will try to implement a custom TargetBuilder then.

Do you know of any plugin I can take a look at that uses this framework to run external compiler?

Artur

0

Please sign in to leave a comment.