Search code examples
javaintellij-ideapants

When running ./pants idea with multiple targets, why doesn't IntelliJ respect my dependencies?


When running ./pants idea on multiple targets, the generated IntelliJ project allows me to reference code that my targets don't actually depend on.

Here's an example from the pants

./pants idea  examples/tests/java/com/pants/examples/hello/greet:: \
    examples/tests/java/com/pants/examples/useproto::

In Greeting.java, I added a dependency on something that the hello project doesn't actually depend on:

  public static String greet(String greetee) {  
    // These two lines added to illustrate question
    Distance proto = Distance.getDefaultInstance();
    System.out.println("Proto: " + proto.toString());
    return "Hello, " + greetee + "!";
  }

But the GreetingTest project runs fine under IntelliJ!

When I try to run GreetingTest from the command line though, it fails.

$ ./pants test examples/tests/java/com/pants/examples/hello/greet
INFO] Detected git repository at /Users/zundel/Src/pants on branch master

11:24:20 00:00 [main]
               (To run a reporting server: ./pants server)
11:24:20 00:00   [bootstrap]
11:24:20 00:00   [setup]
11:24:20 00:00     [parse]
               Executing tasks in goals: bootstrap -> imports -> unpack-jars -> deferred-sources -> gen -> resolve -> compile -> resources -> test
11:24:20 00:00   [bootstrap]
11:24:20 00:00     [bootstrap-jvm-tools]
11:24:20 00:00   [imports]
11:24:20 00:00     [ivy-imports]
11:24:20 00:00   [unpack-jars]
11:24:20 00:00     [unpack-jars]
11:24:20 00:00   [deferred-sources]
11:24:20 00:00     [deferred-sources]
11:24:20 00:00   [gen]
11:24:20 00:00     [thrift]
11:24:20 00:00     [protoc]
11:24:20 00:00     [antlr]
11:24:20 00:00     [ragel]
11:24:20 00:00     [jaxb]
11:24:21 00:01     [wire]
11:24:21 00:01     [aapt]
11:24:21 00:01     [scrooge]
11:24:21 00:01   [resolve]
11:24:21 00:01     [ivy]
                   Invalidated 2 targets.
11:24:21 00:01       [ivy-resolve]
11:24:21 00:01   [compile]
11:24:21 00:01     [compile]
11:24:21 00:01     [jvm]
11:24:21 00:01       [jvm-compilers]
11:24:21 00:01         [find-deleted-sources]
                     Invalidated 2 targets.
11:24:21 00:01         [partition-analysis]
                     Compiling 2 java sources in 2 targets (partition 1 of 1).
11:24:21 00:01         [compile]
11:24:21 00:01           [jmake]
                         Jmake version 1.3.8-10
                         Opening project database...  Done.
                         Recompiling source files:
                         /Users/zundel/Src/pants/examples/src/java/com/pants/examples/hello/greet/Greeting.java
                         /Users/zundel/Src/pants/examples/src/java/com/pants/examples/hello/greet/Greeting.java:6: error: package com.pants.examples.distance.Distances does not exist
                         import com.pants.examples.distance.Distances.Distance;
                                                                     ^
                         /Users/zundel/Src/pants/examples/src/java/com/pants/examples/hello/greet/Greeting.java:37: error: cannot find symbol
                           symbol:   class Distance
                           location: class com.pants.examples.hello.greet.Greeting
                             Distance proto = Distance.getDefaultInstance();
                             ^
                         /Users/zundel/Src/pants/examples/src/java/com/pants/examples/hello/greet/Greeting.java:37: error: cannot find symbol
                           symbol:   variable Distance
                           location: class com.pants.examples.hello.greet.Greeting
                             Distance proto = Distance.getDefaultInstance();
                                              ^
                         Reading existing dependency file at /Users/zundel/Src/pants/.pants.d/compile/jvm/java/jmake-depfiles/global_depfile
                         Writing class dependency file to /Users/zundel/Src/pants/.pants.d/compile/jvm/java/jmake-depfiles/global_depfile
                         Writing project database...  Done.

FAILURE: compilation error


               Waiting for background workers to finish.
               FAILURE

Solution

  • The first thing to note is the fact that test run green in the IntelliJ IDEA project generated by:

    $ ./pants idea \
      examples/tests/java/com/pants/examples/hello/greet:: \
      examples/tests/java/com/pants/examples/useproto::
    

    is a result of the targets you selected on your command line and not the fact you ran the tests in IDEA.

    For example, if you run the tests from the command line like so, they also pass:

    $ ./pants test \
      examples/tests/java/com/pants/examples/hello/greet:: \
      examples/tests/java/com/pants/examples/useproto::`
    

    As you noted, the underlying reason is pants is letting the examples/tests/java/com/pants/examples/hello/greet target to reference code it has not declared a dependency on. This is, in short, a bug in both the pants idea and test goals today.

    1. idea bug

      The idea goal currently generates one module for all targets specified on the command line and this makes all targets visible to each other for the purposes of IDEA's compilation scheme. Instead, the idea goal should really generate a module-per-target. You might try the IDEA Pants Support plugin as an alternative to the ./pants idea command line goal.

    2. test bug

      This is really a bug in the compile goal which test implicitly runs. As things stand, pants handling of jvm compilation is conceptually batch. If you hand it many targets as you did with the :: recursive target globs, it treats the resulting transitive closure of targets as one compilation unit for the purposes of classpaths and source paths. Now this is a white lie and under certain circumstances pants is forced to break up compilation into several chunks and in those cases a glob of targets with undeclared dependencies might also fail.

      There is work underway to have pants move from conceptual batching to real per-target isolated compilation. Once that work is complete, by default or by configuration of your repo's pants.ini, both the globbed test command above and the individual target test command will fail in the compile phase.