How to generate .classpath for a BUILD file using aspects?

762 views
Skip to first unread message

Igor Gatis

unread,
Jul 13, 2018, 8:24:24 AM7/13/18
to bazel-discuss
My organization is migrating to Bazel and the biggest challenge has been lack of IDE integration. In particular, all we want is decent code completion in Java.

TL;DR: how to generate Eclipse .classpath file from BUILD using Bazel (aspects)?

We came up with the following approach:

1. Bazel is the source of truth. That's what we use to build both in our workstations and in CI environment.
2. We want to use Eclipse as code editor and take advantage of it's code completion, formatting, import organization, etc.
3. We settled in the following code structure:
root/
   path1/
      BUILD
      .project
      .classpath
      src/
         com/
            pkg1/
               Source1.java
   path2/
      BUILD
      .project
      .classpath
      src/
         com/
            pkg2/
               Source2.java
4. The files .project and .classpath are to be generated from BUILD.


We wrote .project and .classpath as if they wore generated by a program. This works quite well: we got two projects named after their packages "com.pkg1" and "com.pkg2". Code completion works just as expected. com.pkg2 depends on com.pkg1 so we added a project-reference which enabled smooth code navigation and refactoring capabilities.

The idea is to add a target like eclipse_files(name="eclipse_files") so I can run:

$ bazel build //root/pkg2:eclipse_files


To get files generated. I played with aspects but I still could not grasp how to write tools.bzl's eclipse_files rule/macro/function in order to aggregate deps from java_* rules.


Here is what root/path2/ files look like:

root/path2/.classpath
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry combineaccessrules="false" kind="src" path="/com.pkg1"/>
<classpathentry kind="lib" path="../../bazel-bin/external/com_google_protobuf/libprotobuf_java.jar"/>
<classpathentry kind="lib" path="../../bazel-bin/external/com_google_protobuf/libprotobuf_java_util.jar"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="output" path="bin"/>
</classpath>

root/path2/.project
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>com.acqio.cmdline</name>
<comment></comment>
<projects></projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments></arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>

root/path2/BUILD
package(default_visibility = ["//visibility:public"])

java_library(
    name = "pkg2",
    srcs = glob(
        ["src/**/*.java"],
        exclude = ["src/**/*Test.java"],
    ),
    deps = [
        "//root/pkg1:pkg1",
        "@com_google_protobuf//:protobuf_java",
        "@com_google_protobuf//:protobuf_java_util",
    ],
)

#load("//tools:tools.bzl", "eclipse_files")
#
#eclipse_files(
#    name = 'eclipse_files'
#)

László Csomor

unread,
Jul 13, 2018, 9:48:05 AM7/13/18
to igor...@gmail.com, Dmitry Lomov, bazel-discuss
+Dmitry Lomov : do you have any ideas how to do this?


--
László Csomor | Software Engineer | laszlo...@google.com

Google Germany GmbH | Erika-Mann-Str. 33 | 80636 München | Germany
Registergericht und -nummer: Hamburg, HRB 86891
Sitz der Gesellschaft: Hamburg
Geschäftsführer: Paul Manicle, Halimah DeLaine Prado


--
You received this message because you are subscribed to the Google Groups "bazel-discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to bazel-discus...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/bazel-discuss/503b729f-fd65-449b-90c1-d67fdf1904f8%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Dmitry Lomov

unread,
Jul 13, 2018, 9:57:02 AM7/13/18
to igor...@gmail.com, bazel-discuss
Hi Igor, 
the general ideas around IDE integrations are elaborated in https://blog.bazel.build/2016/06/10/ide-support.html and aspect documentation is https://docs.bazel.build/versions/master/skylark/aspects.html.

The eclipse_file needs to be a rule that sends an aspect down its dependencies and transitively accumulates information about all rules (e.g. in form of some generatd files). An example of aspect implementation is: https://github.com/bazelbuild/eclipse/blob/b9a2bdba988b4183bf063740d7ae62bdd032c7b9/resources/e4b_aspect.bzl

For Eclipse specifically we have https://github.com/bazelbuild/eclipse/ but we do not really support it.

Hope this is enough pointers, and let me know if you have further questions.

Dmitry

On Fri, Jul 13, 2018 at 2:24 PM Igor Gatis <igor...@gmail.com> wrote:
--
You received this message because you are subscribed to the Google Groups "bazel-discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to bazel-discus...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/bazel-discuss/503b729f-fd65-449b-90c1-d67fdf1904f8%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.


--
Google Germany GmbH
Erika-Mann-Straße 33, 80636 München, Germany

Igor Gatis

unread,
Jul 14, 2018, 5:12:57 PM7/14/18
to dsl...@google.com, bazel-...@googlegroups.com
I managed to generate .classpath and .project without aspects. I now include the following target in my BUILD files:

eclipse_project(
    name = "eclipse_project",
    project_name = "pkg2",
    deps = [
        "//root/pkg1:eclipse_project",
    ],
)

and:

eclipse_project(
    name = "eclipse_project",
    project_name = "pkg1",
)

With the command: bazel build //root/pk2


Everything looks neat except by a few dependencies whose path seem wrong. Here is a example of a regular path (fully qualified target => output jar):

//third_party:slf4j
  => third_party/slf4j/slf4j-api-1.7.7.jar
  => third_party/slf4j/slf4j-jdk14-1.7.7.jar
@com_google_protobuf//:protobuf_java
  => bazel-out/k8-fastbuild/bin/external/com_google_protobuf/libprotobuf_java.jar
@io_grpc_grpc_java//core:core
  => bazel-out/k8-fastbuild/bin/external/io_grpc_grpc_java/core/libcore.jar
@io_grpc_grpc_java//netty:netty
  => bazel-out/k8-fastbuild/bin/external/io_grpc_grpc_java/netty/libnetty.jar

In the items above, I see both bazel-out and thrid-party folders in my workspace root. Here are a few examples of wrong path:

@com_google_guava_guava//jar:jar
  => external/com_google_guava_guava/jar/guava-20.0.jar
@io_netty_netty_handler//jar:jar
  => external/io_netty_netty_handler/jar/netty-handler-4.1.25.Final.jar

My workspace root does not have a folder named external (it does under bazel-out/k8-fastbuild/genfiles/external/io_netty_netty_handler_proxy/jar/_ijar/jar/external/io_netty_netty_handler_proxy/jar).

I believe this has to do with maven_jar targets but I could figure that out. In any case, Am I missing something in _list_output_jars?

def _list_output_jars(dep):
  jars = []
  for jar in dep.java.outputs.jars:
    jars.append((str(dep.label), jar.class_jar.path))
  if not jars:
    for file in dep.files:
      if not file.path.endswith("-src.jar"):
        jars.append((str(dep.label), file.path))
  return jars

david.o...@gmail.com

unread,
Jul 15, 2018, 4:11:37 AM7/15/18
to bazel-discuss
You can retrieve the location of external folder using:

$ ls `bazel info output_base`/external

from workspace root. This is what Gerrit Code Review
project does to retrieve location of external folder to
generate .classpath from within python script: [1]

def retrieve_ext_location():
return check_output(_build_bazel_cmd('info', 'output_base')).strip()

We have also extracted this script to bazlets repository,
that is consumed by more than 100 gerrit plugins: [2], for
standalone build mode, e.g.: [3].

[1] https://github.com/GerritCodeReview/gerrit/blob/master/tools/eclipse/project.py#L69-L70
[2] https://github.com/GerritCodeReview/bazlets/blob/master/tools/eclipse/project.py
[3] https://github.com/davido/gerrit-oauth-provider/blob/master/WORKSPACE#L3

Igor Gatis

unread,
Jul 15, 2018, 6:21:44 AM7/15/18
to bazel-discuss
That tells what needs to be added to the path (although I'd need to extract that using Bazel APIs instead). But when should I use that info? Is it safe to check for "external/" path prefix?

Igor Gatis

unread,
Jul 16, 2018, 10:25:21 AM7/16/18
to bazel-...@googlegroups.com
I must have missed something in JavaInfo. It has everything I need. I'm using dep[JavaInfo]'s full_compile_jars and source_jars. Thanks folks.


On Sun, Jul 15, 2018 at 7:21 AM Igor Gatis <igor...@gmail.com> wrote:
That tells what needs to be added to the path (although I'd need to extract that using Bazel APIs instead). But when should I use that info? Is it safe to check for "external/" path prefix?

--
You received this message because you are subscribed to the Google Groups "bazel-discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to bazel-discus...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages