Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,11 @@ local.properties

# Code Recommenders
.recommenders/

# Auto-generated java files
autogenerated/

######################################################################
# Python Ignores

*.pyc
59 changes: 59 additions & 0 deletions java/com/google/domain/registry/eclipse/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package(
default_visibility = ["//java/com/google/domain/registry:registry_project"],
)

java_binary(
name = "annotation_processors_ide",
create_executable = False,
runtime_deps = [
"@auto_factory//jar",
"@auto_service//jar",
"@auto_value//jar",
"@javawriter//jar",
"//third_party/java/dagger:dagger-compiler",
],
)

java_binary(
name = "registry_deps",
create_executable = 0,
runtime_deps = [
"//java/com/google/common/testing",
"//java/com/google/domain/registry/module/frontend",
"//java/com/google/domain/registry/module/backend",
"//java/com/google/domain/registry/module/tools",
"//java/com/google/domain/registry/tools",
"//third_party/java/appengine:appengine-api-link",
"//third_party/java/appengine:appengine-remote-api-link",
"//third_party/java/appengine:appengine-stubs",
"//third_party/java/appengine:appengine-integration-testing",
"//third_party/java/appengine:appengine-testing",
"//third_party/java/apache_sshd",
"//third_party/java/ftpserver",
"//third_party/java/hamcrest",
"//third_party/java/jetty/v6_1_22",
"//third_party/java/junit",
"//third_party/java/mockito",
"//third_party/java/truth",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yes testing dependencies. Makes sense that you'd need them here.

],
)

# This rule creates a copy of the registry_deps_deploy.jar with all of the
# domain-registry code removed. This is to avoid having duplicate instances
# of domain-registry classes on the eclipse build path.
genrule(
name = "eclipse_deps",
srcs = ["//java/com/google/domain/registry/eclipse:registry_deps_deploy.jar"],
outs = ["eclipse_deps.jar"],
cmd = " && ".join([
"IN=$$(pwd)/$(SRCS)",
"OUT=$$(pwd)/$@",
"TMP=$$(mktemp -d $${TMPDIR:-/tmp}/eclipse_deps.XXXXXXXX)",
"cd $$TMP",
"jar -xf $$IN",
"rm -rf com/google/domain/registry",
"jar -cmf META-INF/MANIFEST.MF eclipse_deps.jar .",
"mv eclipse_deps.jar $$OUT",
"rm -rf $$TMP",
]),
)
1 change: 1 addition & 0 deletions java/com/google/domain/registry/tools/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ package_group(
packages = [
"//java/com/google/domain/registry/testing",
"//java/com/google/domain/registry/tools",
"//java/com/google/domain/registry/eclipse",
"//javatests/com/google/domain/registry/tools",
],
)
Expand Down
303 changes: 303 additions & 0 deletions scripts/setup_eclipse.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,303 @@
#!/usr/bin/python

# Copyright 2016 Google Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"Script for generating eclipse .project and .classpath files."

import os
import subprocess
import sys


def bazel_info(key):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't have to bother making this change, since you wrote such good documentation. But in the future, you probably could have made a lot of these methods private (prefix underscore) and then the pydocs wouldn't be required.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had the same thought :)

"""Invokes the bazel info subcommand.

Invokes bazel info on the command line, parses the output and returns it

Args:
key: The argument that is passed to bazel info. See
http://bazel.io/docs/bazel-user-manual.html#info for example values.

Returns:
The output of the bazel info invocation as a string. If multiple lines
are returned by bazel info, only the first line is returned.
"""
bazel_process = subprocess.Popen(
["bazel", "info", key],

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Optional] Here's a purely stylistic thing you may not have considered, which you are 100% free to ignore. A generic method could be created from this defenition: def exec_and_read_lines(args): .... Then you could say bazel_info = λ key: exec_and_read_lines(["bazel", "info", key]).

stdout=subprocess.PIPE,
)
result = [line.strip() for line in iter(bazel_process.stdout.readline, "")]
return result[0]


def classpath_entry_xml(kind, path):
"""
Generates an eclipse xml classpath entry.

Args:
kind: Kind of classpath entry.
Example values are 'lib', 'src', and 'con'
path: Absolute or relative path to the referenced resource.
Paths that are not absolute are relative to the project root.

Returns:
xml classpath entry element with the specified kind and path.
"""
return "<classpathentry kind=\"{kind}\" path=\"{path}\"/>".format(
kind=kind, path=path,
)


def classpath_xml(entries):
"""
Produces the xml for an eclipse classpath file.

Args:
entries: list of dictionaries in the form of:
{
"kind": (str),
"path": (str)
}

Returns:
Contents of the eclipse .classpath file.
"""
entries_xml = "\n".join([
" " + classpath_entry_xml(**entry) for entry in entries])
return ('<?xml version="1.0" encoding="UTF-8"?>\n'
"<classpath>\n"
"{entries}"
"\n</classpath>").format(entries=entries_xml)


def build_classpath():
"""
Builds eclipse classpath file

Generates an eclipse .classpath file that has references to all of the
project source folders, autogenerated source code, and external binary
dependencies.

Returns:
Contents of the eclipse .classpath file.
"""
# source folder for autogenerated files must reference
# symlinked bazel-genfiles folder inside of the project.
bazel_genfiles = bazel_info("bazel-genfiles")
classpath_entries = [
{"kind": "con", "path": "org.eclipse.jdt.launching.JRE_CONTAINER"},
{"kind": "src", "path": "java"},
{"kind": "src", "path": "javatests"},
{"kind": "src", "path": "bazel-genfiles/java"},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about bazel-genfiles/javatests?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question. I don't think there are any generated files in javatests that we need here. Those that are generated are produced by the annotation processors - would you prefer it is added here just for completeness?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure why not? It's better than having us start generating stuff under javatests down the road, and then having to spend two hours figuring out why it doesn't show up in Eclipse.

{
"kind": "lib",
"path": ("%s/java/com/google/domain/"
"registry/eclipse/eclipse_deps.jar" % bazel_genfiles)
},
{"kind": "output", "path": "bin"},
]

return classpath_xml(classpath_entries)


def build_project(project_name):
"""
Builds eclipse project file

Uses a very simple template to generate an eclipse .project file
with a configurable project name.

Args:
project_name: Name of the eclipse project. When importing the project
into an eclipse workspace, this is the name that will be shown.
Returns:
Contents of the eclipse .project file.
"""
template = """<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be super cool if this could generate the config that puts AutoValue, Dagger, and AutoFactory into the annotation processor project settings config.

<name>{project_name}</name>
<comment>
</comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.python.pydev.PyDevBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.python.pydev.pythonNature</nature>
</natures>
</projectDescription>"""
return template.format(project_name=project_name)


def factorypath_entry_xml(kind, entry_id):
"""
Generates an eclipse xml factorypath entry.

Args:
kind: Kind of factorypath entry.
Example values are 'PLUGIN', 'WKSPJAR'
entry_id: Unique identifier for the factorypath entry

Returns:
xml factorypath entry element with the specified kind and id.
"""
return ("<factorypathentry kind=\"{kind}\" id=\"{entry_id}\" "
"enabled=\"true\" runInBatchMode=\"false\"/>").format(
kind=kind, entry_id=entry_id)

def factorypath_xml(entries):
"""
Produces the xml for an eclipse factorypath file.

Args:
entries: list of dictionaries in the form of:
{
"kind": (str),
"entry_id": (str)
}

Returns:
Contents of the eclipse .factorypath file.
"""
entries_xml = "\n".join([
" " + factorypath_entry_xml(**entry) for entry in entries])
return ("<factorypath>\n"
"{entries}"
"\n</factorypath>").format(entries=entries_xml)


def build_factorypath():
"""
Builds eclipse factorypath file

Generates an eclipse .factorypath file that links to the jar containing
all required annotation processors for the project.

Returns:
Contents of the eclipse .factorypath file.
"""
bazel_bin = bazel_info("bazel-bin")
annotations_jar = os.path.join(
bazel_bin,
"java/com/google/domain/registry/eclipse"
"/annotation_processors_ide_deploy.jar",
)
factorypath_entries = [
{
"kind": "PLUGIN",
"entry_id": "org.eclipse.jst.ws.annotations.core",
},
{
"kind": "EXTJAR",
"entry_id": annotations_jar,
}
]
return factorypath_xml(factorypath_entries)


def build_dependencies():
"""
Builds dependencies for producing eclipse project files

Runs bazel build for the entire project and builds a single jar with all
binary dependencies for eclipse to compile the project.

Raises:
subprocess.CalledProcessError: A bazel build failed
"""
# Build entire project first
subprocess.check_call([
"bazel",
"build",
"//java/com/google/domain/registry/...",
"//javatests/com/google/domain/registry/..."])

# Builds a giant jar of all compile-time dependencies of the project
subprocess.check_call([
"bazel",
"build",
"//java/com/google/domain/registry/eclipse:eclipse_deps",
])

# Builds a jar with all annotation processors
subprocess.check_call([
"bazel",
"build",
"//java/com/google/domain/registry/eclipse"
":annotation_processors_ide_deploy.jar"
])

def main():
"""
Builds eclipse project files

Before building the eclipse files, a working bazel build is required.
After building the eclipse dependencies jar and the tests, eclipse
project files are produced.
"""
build_dependencies()
workspace_directory = bazel_info("workspace")
classpath = build_classpath()
with open(os.path.join(
workspace_directory, ".classpath"), "w") as classpath_file:
classpath_file.write(classpath)
if len(sys.argv) > 1:
project_name = sys.argv[1]
else:
project_name = "domain-registry"
project = build_project(project_name)
with open(os.path.join(workspace_directory, ".project"),
"w") as project_file:
project_file.write(project)
factorypath = build_factorypath()
with open(os.path.join(workspace_directory, ".factorypath"),
"w") as factorypath_file:
factorypath_file.write(factorypath)
if not os.path.exists(".settings"):
os.makedirs(".settings")
# XXX: Avoid wiping out existing settings from org.eclipse.jdt.core.prefs
with open(os.path.join(
workspace_directory,
".settings",
"org.eclipse.jdt.core.prefs"), "w") as prefs_file:
prefs_file.write("\n".join([
"eclipse.preferences.version=1",
"org.eclipse.jdt.core.compiler.processAnnotations=enabled",
]))
with open(os.path.join(
workspace_directory,
".settings",
"org.eclipse.jdt.apt.core.prefs"), "w") as prefs_file:
prefs_file.write("\n".join([
"eclipse.preferences.version=1",
"org.eclipse.jdt.apt.aptEnabled=true",
"org.eclipse.jdt.apt.genSrcDir=autogenerated",
"org.eclipse.jdt.apt.reconcileEnabled=true",
]))


if __name__ == "__main__":
main()
1 change: 1 addition & 0 deletions third_party/java/dagger/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ java_library(
java_library(
name = "dagger-compiler",
exports = ["@dagger_compiler//jar"],
visibility = ["//visibility:public"],
runtime_deps = [
"@gson//jar",
"@guava//jar",
Expand Down