diff --git a/.bazelrc.travis b/.bazelrc.travis index 83bf60532..657602435 100644 --- a/.bazelrc.travis +++ b/.bazelrc.travis @@ -17,7 +17,7 @@ build --verbose_failures # runs stuff in a container, and since Travis already runs its script # in a container (unless you require sudo in your .travis.yml) this # fails to run tests. -build --spawn_strategy=standalone --genrule_strategy=standalone +build --spawn_strategy=standalone --genrule_strategy=standalone --strategy=Scalac=worker --strategy=ScroogeRule=worker --worker_max_instances=3 test --test_strategy=standalone # Below this line, .travis.yml will cat the default bazelrc. diff --git a/.travis.yml b/.travis.yml index 477557d78..e4cf4ff9b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,7 +14,7 @@ env: # we want to test the most recent few releases - V=0.5.3 - V=0.5.4 - - V=0.6.0rc2 + - V=0.6.0 before_install: - | diff --git a/README.md b/README.md index 1ddf58aee..8dfa7bc8b 100644 --- a/README.md +++ b/README.md @@ -467,6 +467,71 @@ thrift_library(name, srcs, deps, absolute_prefix, absolute_prefixes) +## scalapb_proto_library + +```python +load("//scala_proto:scala_proto.bzl", "scalapb_proto_library") +scalapb_proto_library(name, deps, with_grpc, with_java, with_flat_package, with_single_line_to_string) +``` + +`scalapb_proto_library` generates a scala library of scala proto bindings +generated by the [ScalaPB compiler](https://github.com/scalapb/ScalaPB). + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Attributes
name +

Name, required

+

A unique name for this target

+
deps +

List of labels, required

+

List of proto dependencies that this target depends on. Must be of type proto_library

+
with_grpc +

boolean; optional (default False)

+

Enables generation of grpc service bindings for services defined in deps

+
with_java +

boolean; optional (default False)

+

Enables generation of converters to and from java protobuf bindings

+
with_flat_package +

boolean; optional (default False)

+

When true, ScalaPB will not append the protofile base name to the package name

+
with_single_line_to_string +

boolean; optional (default False)

+

Enables generation of toString() methods that use the single line format

+
+ ## Building from source Test & Build: ``` diff --git a/WORKSPACE b/WORKSPACE index 25af75840..85e53d71e 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -14,6 +14,9 @@ tut_repositories() load("//jmh:jmh.bzl", "jmh_repositories") jmh_repositories() +load("//scala_proto:scala_proto.bzl", "scala_proto_repositories") +scala_proto_repositories() + load("//specs2:specs2_junit.bzl","specs2_junit_repositories") specs2_junit_repositories() @@ -50,3 +53,17 @@ maven_jar( artifact = "org.apache.commons:commons-lang3:3.5", sha1 = "6c6c702c89bfff3cd9e80b04d668c5e190d588c6" ) + +http_archive( + name = "com_google_protobuf", + urls = ["https://github.com/google/protobuf/archive/b4b0e304be5a68de3d0ee1af9b286f958750f5e4.zip"], + strip_prefix = "protobuf-b4b0e304be5a68de3d0ee1af9b286f958750f5e4", + sha256 = "ff771a662fb6bd4d3cc209bcccedef3e93980a49f71df1e987f6afa3bcdcba3a", +) + +http_archive( + name = "com_google_protobuf_java", + urls = ["https://github.com/google/protobuf/archive/b4b0e304be5a68de3d0ee1af9b286f958750f5e4.zip"], + strip_prefix = "protobuf-b4b0e304be5a68de3d0ee1af9b286f958750f5e4", + sha256 = "ff771a662fb6bd4d3cc209bcccedef3e93980a49f71df1e987f6afa3bcdcba3a", +) diff --git a/scala_proto/BUILD b/scala_proto/BUILD new file mode 100644 index 000000000..e69de29bb diff --git a/scala_proto/scala_proto.bzl b/scala_proto/scala_proto.bzl new file mode 100644 index 000000000..19fb31b7f --- /dev/null +++ b/scala_proto/scala_proto.bzl @@ -0,0 +1,463 @@ +load("//scala:scala.bzl", + "scala_library") + +def scala_proto_repositories(): + native.maven_server( + name = "scala_proto_deps_maven_server", + url = "http://central.maven.org/maven2/", + ) + + native.maven_jar( + name = "scala_proto_rules_protoc_jar", + artifact = "com.github.os72:protoc-jar:3.2.0", + sha1 = "7c06b12068193bd2080caf45580b0a00d2a31638", + server = "scala_proto_deps_maven_server", + ) + + native.bind( + name = 'io_bazel_rules_scala/dependency/proto/protoc', + actual = '@scala_proto_rules_protoc_jar//jar' + ) + + native.maven_jar( + name = "scala_proto_rules_scalapb_plugin", + artifact = "com.trueaccord.scalapb:compilerplugin_2.11:0.6.5", + sha1 = "290094c632c95b36b6f66d7dbfdc15242b9a247f", + server = "scala_proto_deps_maven_server", + ) + + native.bind( + name = 'io_bazel_rules_scala/dependency/proto/scalapb_plugin', + actual = '@scala_proto_rules_scalapb_plugin//jar' + ) + + native.maven_jar( + name = "scala_proto_rules_protoc_bridge", + artifact = "com.trueaccord.scalapb:protoc-bridge_2.11:0.3.0-M1", + sha1 = "73d38f045ea8f09cc1264991d1064add6eac9e00", + server = "scala_proto_deps_maven_server", + ) + + native.bind( + name = 'io_bazel_rules_scala/dependency/proto/protoc_bridge', + actual = '@scala_proto_rules_protoc_bridge//jar' + ) + + native.maven_jar( + name = "scala_proto_rules_scalapbc", + artifact = "com.trueaccord.scalapb:scalapbc_2.11:0.6.5", + sha1 = "b204d6d56a042b973af5b6fe28f81ece232d1fe4", + server = "scala_proto_deps_maven_server", + ) + + native.bind( + name = 'io_bazel_rules_scala/dependency/proto/scalapbc', + actual = '@scala_proto_rules_scalapbc//jar' + ) + + native.maven_jar( + name = "scala_proto_rules_scalapb_runtime", + artifact = "com.trueaccord.scalapb:scalapb-runtime_2.11:0.6.5", + sha1 = "ac9287ff48c632df525773570ee4842e3ddf40e9", + server = "scala_proto_deps_maven_server", + ) + + native.bind( + name = 'io_bazel_rules_scala/dependency/proto/scalapb_runtime', + actual = '@scala_proto_rules_scalapb_runtime//jar' + ) + + native.maven_jar( + name = "scala_proto_rules_scalapb_runtime_grpc", + artifact = "com.trueaccord.scalapb:scalapb-runtime-grpc_2.11:0.6.5", + sha1 = "9dc3374001f4190548db36a7dc87bd4f9bca6f9c", + server = "scala_proto_deps_maven_server", + ) + + native.bind( + name = 'io_bazel_rules_scala/dependency/proto/scalapb_runtime_grpc', + actual = '@scala_proto_rules_scalapb_runtime_grpc//jar' + ) + + native.maven_jar( + name = "scala_proto_rules_scalapb_lenses", + artifact = "com.trueaccord.lenses:lenses_2.11:0.4.12", + sha1 = "c5fbf5b872ce99d9a16d3392ccc0d15a0e43d823", + server = "scala_proto_deps_maven_server", + ) + + native.bind( + name = 'io_bazel_rules_scala/dependency/proto/scalapb_lenses', + actual = '@scala_proto_rules_scalapb_lenses//jar' + ) + + native.maven_jar( + name = "scala_proto_rules_scalapb_fastparse", + artifact = "com.lihaoyi:fastparse_2.11:0.4.4", + sha1 = "f065fe0afe6fd2b4557d985c37362c36f08f9947", + server = "scala_proto_deps_maven_server", + ) + + native.bind( + name = 'io_bazel_rules_scala/dependency/proto/scalapb_fastparse', + actual = '@scala_proto_rules_scalapb_fastparse//jar' + ) + + native.maven_jar( + name = "scala_proto_rules_grpc_core", + artifact = "io.grpc:grpc-core:1.3.1", + sha1 = "a9b38b4a19af3ef208f4f6bf7871876d959c5eb1", + server = "scala_proto_deps_maven_server", + ) + + native.bind( + name = 'io_bazel_rules_scala/dependency/proto/grpc_core', + actual = '@scala_proto_rules_grpc_core//jar' + ) + + native.maven_jar( + name = "scala_proto_rules_grpc_stub", + artifact = "io.grpc:grpc-stub:1.3.1", + sha1 = "60bdfa9d8c664a9d87ae461106eff6eed8da6c54", + server = "scala_proto_deps_maven_server", + ) + + native.bind( + name = 'io_bazel_rules_scala/dependency/proto/grpc_stub', + actual = '@scala_proto_rules_grpc_stub//jar' + ) + + native.maven_jar( + name = "scala_proto_rules_grpc_protobuf", + artifact = "io.grpc:grpc-protobuf:1.3.1", + sha1 = "9562e977cacd6e128a31686c3e6948d61873c496", + server = "scala_proto_deps_maven_server", + ) + + native.bind( + name = 'io_bazel_rules_scala/dependency/proto/grpc_protobuf', + actual = '@scala_proto_rules_grpc_protobuf//jar' + ) + + native.maven_jar( + name = "scala_proto_rules_grpc_netty", + artifact = "io.grpc:grpc-netty:1.3.1", + sha1 = "cc3831fccb76cfe21445f75cc055b5ffd979dc54", + server = "scala_proto_deps_maven_server", + ) + + native.bind( + name = 'io_bazel_rules_scala/dependency/proto/grpc_netty', + actual = '@scala_proto_rules_grpc_netty//jar' + ) + + native.maven_jar( + name = "scala_proto_rules_grpc_context", + artifact = "io.grpc:grpc-context:1.3.1", + sha1 = "28accd419b18d59055b8999f78f5cb7767c7bde8", + server = "scala_proto_deps_maven_server", + ) + + native.bind( + name = 'io_bazel_rules_scala/dependency/proto/grpc_context', + actual = '@scala_proto_rules_grpc_context//jar' + ) + + native.maven_jar( + name = "scala_proto_rules_guava", + artifact = "com.google.guava:guava:19.0", + sha1 = "6ce200f6b23222af3d8abb6b6459e6c44f4bb0e9", + server = "scala_proto_deps_maven_server", + ) + + native.bind( + name = 'io_bazel_rules_scala/dependency/proto/guava', + actual = '@scala_proto_rules_guava//jar' + ) + + native.maven_jar( + name = "scala_proto_rules_google_instrumentation", + artifact = "com.google.instrumentation:instrumentation-api:0.3.0", + sha1 = "a2e145e7a7567c6372738f5c5a6f3ba6407ac354", + server = "scala_proto_deps_maven_server", + ) + + native.bind( + name = 'io_bazel_rules_scala/dependency/proto/google_instrumentation', + actual = '@scala_proto_rules_google_instrumentation//jar' + ) + + native.maven_jar( + name = "scala_proto_rules_netty_codec", + artifact = "io.netty:netty-codec:4.1.8.Final", + sha1 = "1bd0a2d032e5c7fc3f42c1b483d0f4c57eb516a3", + server = "scala_proto_deps_maven_server", + ) + + native.bind( + name = 'io_bazel_rules_scala/dependency/proto/netty_codec', + actual = '@scala_proto_rules_netty_codec//jar' + ) + + native.maven_jar( + name = "scala_proto_rules_netty_codec_http", + artifact = "io.netty:netty-codec-http:4.1.8.Final", + sha1 = "1e88617c4a6c88da7e86fdbbd9494d22a250c879", + server = "scala_proto_deps_maven_server", + ) + + native.bind( + name = 'io_bazel_rules_scala/dependency/proto/netty_codec_http', + actual = '@scala_proto_rules_netty_codec_http//jar' + ) + + native.maven_jar( + name = "scala_proto_rules_netty_codec_socks", + artifact = "io.netty:netty-codec-socks:4.1.8.Final", + sha1 = "7f7c5f5b154646d7c571f8ca944fb813f71b1d51", + server = "scala_proto_deps_maven_server", + ) + + native.bind( + name = 'io_bazel_rules_scala/dependency/proto/netty_codec_socks', + actual = '@scala_proto_rules_netty_codec_socks//jar' + ) + + native.maven_jar( + name = "scala_proto_rules_netty_codec_http2", + artifact = "io.netty:netty-codec-http2:4.1.8.Final", + sha1 = "105a99ee5767463370ccc3d2e16800bd99f5648e", + server = "scala_proto_deps_maven_server", + ) + + native.bind( + name = 'io_bazel_rules_scala/dependency/proto/netty_codec_http2', + actual = '@scala_proto_rules_netty_codec_http2//jar' + ) + + native.maven_jar( + name = "scala_proto_rules_netty_handler", + artifact = "io.netty:netty-handler:4.1.8.Final", + sha1 = "db01139bfb11afd009a695eef55b43bbf22c4ef5", + server = "scala_proto_deps_maven_server", + ) + + native.bind( + name = 'io_bazel_rules_scala/dependency/proto/netty_handler', + actual = '@scala_proto_rules_netty_handler//jar' + ) + + native.maven_jar( + name = "scala_proto_rules_netty_buffer", + artifact = "io.netty:netty-buffer:4.1.8.Final", + sha1 = "43292c2622e340a0d07178c341ca3bdf3d662034", + server = "scala_proto_deps_maven_server", + ) + + native.bind( + name = 'io_bazel_rules_scala/dependency/proto/netty_buffer', + actual = '@scala_proto_rules_netty_buffer//jar' + ) + + native.maven_jar( + name = "scala_proto_rules_netty_transport", + artifact = "io.netty:netty-transport:4.1.8.Final", + sha1 = "905b5dadce881c9824b3039c0df36dabbb7b6a07", + server = "scala_proto_deps_maven_server", + ) + + native.bind( + name = 'io_bazel_rules_scala/dependency/proto/netty_transport', + actual = '@scala_proto_rules_netty_transport//jar' + ) + + native.maven_jar( + name = "scala_proto_rules_netty_resolver", + artifact = "io.netty:netty-resolver:4.1.8.Final", + sha1 = "2e116cdd5edc01b2305072b1dbbd17c0595dbfef", + server = "scala_proto_deps_maven_server", + ) + + native.bind( + name = 'io_bazel_rules_scala/dependency/proto/netty_resolver', + actual = '@scala_proto_rules_netty_resolver//jar' + ) + + native.maven_jar( + name = "scala_proto_rules_netty_common", + artifact = "io.netty:netty-common:4.1.8.Final", + sha1 = "ee62c80318413d2375d145e51e48d7d35c901324", + server = "scala_proto_deps_maven_server", + ) + + native.bind( + name = 'io_bazel_rules_scala/dependency/proto/netty_common', + actual = '@scala_proto_rules_netty_common//jar' + ) + + native.maven_jar( + name = "scala_proto_rules_netty_handler_proxy", + artifact = "io.netty:netty-handler-proxy:4.1.8.Final", + sha1 = "c4d22e8b9071a0ea8d75766d869496c32648a758", + server = "scala_proto_deps_maven_server", + ) + + native.bind( + name = 'io_bazel_rules_scala/dependency/proto/netty_handler_proxy', + actual = '@scala_proto_rules_netty_handler_proxy//jar' + ) + +def _colon_paths(data): + return ':'.join([f.path for f in data]) + +def _gen_proto_srcjar_impl(ctx): + acc_imports = depset() + for target in ctx.attr.deps: + acc_imports += target.proto.transitive_sources + + # Command line args to worker cannot be empty so using padding + flags = ["-"] + if ctx.attr.with_grpc: + flags.append("grpc") + if ctx.attr.with_java: + flags.append("java_conversions") + if ctx.attr.with_flat_package: + flags.append("flat_package") + if ctx.attr.with_single_line_to_string: + flags.append("single_line_to_string") + + worker_content = "{output}\n{paths}\n{flags_arg}".format( + output = ctx.outputs.srcjar.path, + paths = _colon_paths(acc_imports), + flags_arg = ",".join(flags), + ) + argfile = ctx.new_file(ctx.outputs.srcjar, "%s_worker_input" % ctx.label.name) + ctx.file_action(output=argfile, content=worker_content) + ctx.action( + executable = ctx.executable._pluck_scalapb_scala, + inputs = list(acc_imports) + [argfile], + outputs = [ctx.outputs.srcjar], + mnemonic="ProtoScalaPBRule", + progress_message = "creating scalapb files %s" % ctx.label, + execution_requirements={"supports-workers": "1"}, + arguments=["@" + argfile.path], + ) + srcjarsattr = struct( + srcjar = ctx.outputs.srcjar, + ) + return struct( + srcjars=srcjarsattr, + extra_information=[struct( + srcjars=srcjarsattr, + )], + ) + +scalapb_proto_srcjar = rule( + _gen_proto_srcjar_impl, + attrs={ + "deps": attr.label_list( + mandatory=True, + allow_rules=["proto_library"] + ), + "with_grpc": attr.bool(default=False), + "with_java": attr.bool(default=False), + "with_flat_package": attr.bool(default=False), + "with_single_line_to_string": attr.bool(default=False), + "_pluck_scalapb_scala": attr.label( + executable=True, + cfg="host", + default=Label("//src/scala/scripts:scalapb_generator"), + allow_files=True + ), + }, + outputs={ + "srcjar": "lib%{name}.srcjar", + }, +) + +SCALAPB_DEPS = [ + "//external:io_bazel_rules_scala/dependency/proto/scalapb_runtime", + "//external:io_bazel_rules_scala/dependency/com_google_protobuf/protobuf_java", + "//external:io_bazel_rules_scala/dependency/proto/scalapb_lenses", + "//external:io_bazel_rules_scala/dependency/proto/scalapb_fastparse", +] + +GRPC_DEPS = [ + "//external:io_bazel_rules_scala/dependency/proto/scalapb_runtime_grpc", + "//external:io_bazel_rules_scala/dependency/proto/grpc_core", + "//external:io_bazel_rules_scala/dependency/proto/grpc_stub", + "//external:io_bazel_rules_scala/dependency/proto/grpc_protobuf", + "//external:io_bazel_rules_scala/dependency/proto/grpc_netty", + "//external:io_bazel_rules_scala/dependency/proto/grpc_context", + "//external:io_bazel_rules_scala/dependency/proto/guava", + "//external:io_bazel_rules_scala/dependency/proto/google_instrumentation", + "//external:io_bazel_rules_scala/dependency/proto/netty_codec", + "//external:io_bazel_rules_scala/dependency/proto/netty_codec_http", + "//external:io_bazel_rules_scala/dependency/proto/netty_codec_http2", + "//external:io_bazel_rules_scala/dependency/proto/netty_codec_socks", + "//external:io_bazel_rules_scala/dependency/proto/netty_handler", + "//external:io_bazel_rules_scala/dependency/proto/netty_buffer", + "//external:io_bazel_rules_scala/dependency/proto/netty_transport", + "//external:io_bazel_rules_scala/dependency/proto/netty_resolver", + "//external:io_bazel_rules_scala/dependency/proto/netty_common", + "//external:io_bazel_rules_scala/dependency/proto/netty_handler_proxy", +] + +"""Generate scalapb bindings for a set of proto_library targets. + +Example: + scalapb_proto_library( + name = "exampla_proto_scala", + with_grpc = True, + deps = ["//src/proto:example_service"] + ) + +Args: + name: A unique name for this rule + deps: Proto library targets that this rule depends on (must be of type proto_library) + with_grpc: Enables generation of grpc service bindings for services defined in deps + with_java: Enables generation of converters to and from java protobuf bindings + with_flat_package: When true, ScalaPB will not append the protofile base name to the package name + with_single_line_to_string: Enables generation of toString() methods that use the single line format + +Outputs: + A scala_library rule that includes the generated scalapb bindings, as + well as any library dependencies needed to compile and use these. +""" +def scalapb_proto_library( + name, + deps = [], + with_grpc = False, + with_java = False, + with_flat_package = False, + with_single_line_to_string = False, + visibility = None): + + srcjar = name + '_srcjar' + scalapb_proto_srcjar( + name = srcjar, + with_grpc = with_grpc, + with_java = with_java, + with_flat_package = with_flat_package, + with_single_line_to_string = with_single_line_to_string, + deps = deps, + visibility = visibility, + ) + + external_deps = list(SCALAPB_DEPS + GRPC_DEPS if (with_grpc) else SCALAPB_DEPS) + + if with_java: + java_proto_lib = name + "_java_lib" + native.java_proto_library( + name = java_proto_lib, + deps = deps, + ) + external_deps.append(java_proto_lib) + + scala_library( + name = name, + srcs = [srcjar], + deps = external_deps, + exports = external_deps, + visibility = visibility, + ) diff --git a/src/scala/scripts/BUILD b/src/scala/scripts/BUILD index ec3384a15..13787f7cb 100644 --- a/src/scala/scripts/BUILD +++ b/src/scala/scripts/BUILD @@ -21,3 +21,29 @@ scala_binary( ":generator_lib", ], ) + +scala_library( + name = "scalapb_generator_lib", + srcs = ["ScalaPBGenerator.scala"], + visibility = ["//visibility:public"], + deps = [ + "//external:io_bazel_rules_scala/dependency/proto/protoc", + "//external:io_bazel_rules_scala/dependency/proto/scalapb_plugin", + "//external:io_bazel_rules_scala/dependency/proto/protoc_bridge", + "//external:io_bazel_rules_scala/dependency/proto/scalapbc", + "//external:io_bazel_rules_scala/dependency/com_google_protobuf/protobuf_java", + "//src/java/io/bazel/rulesscala/io_utils", + "//src/java/io/bazel/rulesscala/jar", + "//src/java/io/bazel/rulesscala/worker", + "//src/scala/io/bazel/rules_scala/scrooge_support:compiler", + ], +) + +scala_binary( + name = "scalapb_generator", + main_class = "scripts.ScalaPBWorker", + visibility = ["//visibility:public"], + deps = [ + ":scalapb_generator_lib", + ], +) diff --git a/src/scala/scripts/ScalaPBGenerator.scala b/src/scala/scripts/ScalaPBGenerator.scala new file mode 100644 index 000000000..ad6ce2db5 --- /dev/null +++ b/src/scala/scripts/ScalaPBGenerator.scala @@ -0,0 +1,72 @@ +package scripts + +import io.bazel.rulesscala.jar.JarCreator +import io.bazel.rulesscala.io_utils.DeleteRecursively +import java.io.{ File, PrintStream } +import java.nio.file.{ Files, Path, Paths } +import scala.collection.mutable.Buffer +import io.bazel.rulesscala.worker.{ GenericWorker, Processor } +import scala.io.Source +import protocbridge.ProtocBridge +import scalapb.ScalaPbCodeGenerator +import com.trueaccord.scalapb.{Config, ScalaPBC, ScalaPbcException} + +object ScalaPBWorker extends GenericWorker(new ScalaPBGenerator) { + + override protected def setupOutput(ps: PrintStream): Unit = { + System.setOut(ps) + System.setErr(ps) + Console.setErr(ps) + Console.setOut(ps) + } + + def main(args: Array[String]) { + try run(args) + catch { + case x: Exception => + x.printStackTrace() + System.exit(1) + } + } +} + +class ScalaPBGenerator extends Processor { + def deleteDir(path: Path): Unit = + try DeleteRecursively.run(path) + catch { + case e: Exception => () + } + + def processRequest(args: java.util.List[String]) { + val jarOutput = args.get(0) + val protoFiles = args.get(1).split(':').toList + val flagOpt = args.get(2) match { + case "-" => None + case s => Some(s.drop(2)) + } + + val tmp = Paths.get(Option(System.getProperty("java.io.tmpdir")).getOrElse("/tmp")) + val scalaPBOutput = Files.createTempDirectory(tmp, "bazelscalapb") + val flagPrefix = flagOpt.map(_ + ":").getOrElse("") + val scalaPBArgs = s"--scala_out=${flagPrefix}${scalaPBOutput}" :: protoFiles + val config = ScalaPBC.processArgs(scalaPBArgs.toArray) + val code = ProtocBridge.runWithGenerators( + protoc = a => com.github.os72.protocjar.Protoc.runProtoc(config.version +: a.toArray), + namedGenerators = Seq("scala" -> ScalaPbCodeGenerator), + params = config.args) + + val dirsToDelete = Set(scalaPBOutput) + + try { + if (!config.throwException) { + JarCreator.buildJar(Array(jarOutput, scalaPBOutput.toString)) + } else { + if (code != 0) { + throw new ScalaPbcException(s"Exit with code $code") + } + } + } finally { + dirsToDelete.foreach { deleteDir(_) } + } + } +} diff --git a/src/scala/scripts/TwitterScroogeGenerator.scala b/src/scala/scripts/TwitterScroogeGenerator.scala index 9f6a1e68d..f2b53cd65 100644 --- a/src/scala/scripts/TwitterScroogeGenerator.scala +++ b/src/scala/scripts/TwitterScroogeGenerator.scala @@ -73,7 +73,7 @@ class ScroogeGenerator extends Processor { // Further configuration options for scrooge. val additionalFlags = getIdx(5) - val tmp = Paths.get(Option(System.getenv("TMPDIR")).getOrElse("/tmp")) + val tmp = Paths.get(Option(System.getProperty("java.io.tmpdir")).getOrElse("/tmp")) val scroogeOutput = Files.createTempDirectory(tmp, "scrooge") val scrooge = new Compiler diff --git a/test/BUILD b/test/BUILD index a7933194a..c7d34683d 100644 --- a/test/BUILD +++ b/test/BUILD @@ -11,6 +11,9 @@ load("//scala:scala.bzl", "scala_junit_test", "scala_specs2_junit_test") +load("//scala_proto:scala_proto.bzl", + "scalapb_proto_library") + # The examples below show how to combine Scala and Java rules. # ScalaBinary is the Scala equivalent of JavaBinary. @@ -432,3 +435,33 @@ scala_binary( main_class = "scala.test.BinaryDependentOnJava", deps = [":JavaOnlySources"], ) + +scalapb_proto_library( + name = "test_proto_nogrpc", + deps = ["//test/proto:test2"] +) + +scalapb_proto_library( + name = "test_proto_java_conversions", + deps = ["//test/proto:test2", "//test/proto:test"], + with_java = True, + with_flat_package = True, +) + +scalapb_proto_library( + name = "test_proto", + with_grpc = True, + deps = ["//test/proto:test_service"] +) + +scala_library( + name = "lib_with_scala_proto_dep", + srcs = ["TestServer.scala"], + deps = [":test_proto"] +) + +scala_binary( + name = "test_scala_proto_server", + main_class = "test.proto.TestServer", + deps = [":lib_with_scala_proto_dep"] +) diff --git a/test/TestServer.scala b/test/TestServer.scala new file mode 100644 index 000000000..3a44df27f --- /dev/null +++ b/test/TestServer.scala @@ -0,0 +1,98 @@ +package test.proto + +import test.{TestRequest, TestMessage} +import test2.TestResponse1 +import test3.TestResponse2 +import test_service.TestServiceGrpc +import test_service.TestServiceGrpc.TestServiceBlockingStub + +import io.grpc.{Server, ServerBuilder, StatusRuntimeException, ManagedChannelBuilder, ManagedChannel} + +import java.util.concurrent.TimeUnit +import java.util.logging.{Level, Logger} + +import scala.concurrent.{ExecutionContext, Future} + +/** + * Adapted from https://github.com/xuwei-k/grpc-scala-sample/blob/master/grpc-scala/src/main/scala/io/grpc/examples/helloworld/HelloWorldServer.scala + */ +object TestServer { + private val logger = Logger.getLogger(classOf[TestServer].getName) + + def main(args: Array[String]): Unit = { + val server = new TestServer(ExecutionContext.global) + server.start() + val client = TestClient("localhost", 50051) + val msg = args.headOption.getOrElse("ping") + client.send(msg) + client.shutdown() + server.stop() + } + + private val port = 50051 +} + +class TestServer(executionContext: ExecutionContext) { self => + lazy val server: Server = ServerBuilder + .forPort(TestServer.port) + .addService(TestServiceGrpc.bindService(new TestServiceImpl, executionContext)) + .build + + private def start(): Unit = { + server.start() + TestServer.logger.info(s"Server started, listening on ${TestServer.port}") + sys.addShutdownHook { + System.err.println("*** shutting down gRPC server since JVM is shutting down") + self.stop() + System.err.println("*** server shut down") + } + } + + private def stop(): Unit = server.shutdown() + + private def blockUntilShutdown(): Unit = server.awaitTermination() + + private class TestServiceImpl extends TestServiceGrpc.TestService { + override def testMethod1(request: TestRequest): Future[TestResponse1] = { + val response = TestResponse1().withTestMsg(TestMessage(Some("foo"))) + Future.successful(response) + } + + override def testMethod2(request: TestRequest): Future[TestResponse2] = { + val response = TestResponse2().withTestMsg(TestMessage(Some("bar"))) + Future.successful(response) + } + } +} + +object TestClient { + def apply(host: String, port: Int): TestClient = { + val channel = ManagedChannelBuilder.forAddress(host, port).usePlaintext(true).build + val blockingStub = TestServiceGrpc.blockingStub(channel) + new TestClient(channel, blockingStub) + } +} + +class TestClient private( + private val channel: ManagedChannel, + private val blockingStub: TestServiceBlockingStub +) { + private[this] val logger = Logger.getLogger(classOf[TestClient].getName) + + def shutdown(): Unit = { + channel.shutdown.awaitTermination(5, TimeUnit.SECONDS) + } + + def send(name: String): Unit = { + logger.info("Will try to send " + name + " ...") + val request = TestRequest() + try { + val response = blockingStub.testMethod1(request) + logger.info("Response: " + response.testMsg) + } + catch { + case e: StatusRuntimeException => + logger.log(Level.WARNING, "RPC failed: {0}", e.getStatus) + } + } +} diff --git a/test/proto/BUILD b/test/proto/BUILD new file mode 100644 index 000000000..cd4f7176a --- /dev/null +++ b/test/proto/BUILD @@ -0,0 +1,26 @@ +proto_library( + name = "test", + srcs = ["test.proto"], + visibility = ["//visibility:public"], +) + +proto_library( + name = "test2", + deps = [":test"], + srcs = ["test2.proto"], + visibility = ["//visibility:public"], +) + +proto_library( + name = "test3", + deps = [":test"], + srcs = ["test3.proto"], + visibility = ["//visibility:public"], +) + +proto_library( + name = "test_service", + deps = [":test2", ":test3", ":test"], + srcs = ["test_service.proto"], + visibility = ["//visibility:public"], +) diff --git a/test/proto/test.proto b/test/proto/test.proto new file mode 100644 index 000000000..6a51d45b2 --- /dev/null +++ b/test/proto/test.proto @@ -0,0 +1,12 @@ +syntax = "proto2"; + +option java_package = "test.proto"; + +message TestRequest { + optional uint32 a = 1; + optional bool b = 2; +} + +message TestMessage { + optional string foo = 1; +} diff --git a/test/proto/test2.proto b/test/proto/test2.proto new file mode 100644 index 000000000..af4cc4757 --- /dev/null +++ b/test/proto/test2.proto @@ -0,0 +1,10 @@ +syntax = "proto2"; + +import "test/proto/test.proto"; +option java_package = "test.proto"; + +message TestResponse1 { + optional uint32 c = 1; + optional bool d = 2; + optional TestMessage testMsg = 3; +} diff --git a/test/proto/test3.proto b/test/proto/test3.proto new file mode 100644 index 000000000..779e02203 --- /dev/null +++ b/test/proto/test3.proto @@ -0,0 +1,10 @@ +syntax = "proto2"; + +import "test/proto/test.proto"; +option java_package = "test.proto"; + +message TestResponse2 { + optional uint32 e = 1; + optional bool f = 2; + optional TestMessage testMsg = 3; +} diff --git a/test/proto/test_service.proto b/test/proto/test_service.proto new file mode 100644 index 000000000..fd9f95dd0 --- /dev/null +++ b/test/proto/test_service.proto @@ -0,0 +1,12 @@ +syntax = "proto2"; + +import "test/proto/test.proto"; +import "test/proto/test2.proto"; +import "test/proto/test3.proto"; + +option java_package = "test.proto"; + +service TestService { + rpc TestMethod1 (TestRequest) returns (TestResponse1) {} + rpc TestMethod2 (TestRequest) returns (TestResponse2) {} +} diff --git a/test_run.sh b/test_run.sh index 17f92582b..e5bc24f01 100755 --- a/test_run.sh +++ b/test_run.sh @@ -582,3 +582,4 @@ $runner test_scala_library_expect_no_recompilation_on_internal_change_of_scala_d $runner test_scala_library_expect_no_recompilation_on_internal_change_of_java_dependency $runner test_scala_library_expect_no_java_recompilation_on_internal_change_of_scala_sibling $runner test_scala_library_expect_failure_on_missing_direct_java +$runner bazel run test:test_scala_proto_server diff --git a/twitter_scrooge/twitter_scrooge.bzl b/twitter_scrooge/twitter_scrooge.bzl index 68fa1713b..c11e23d7d 100644 --- a/twitter_scrooge/twitter_scrooge.bzl +++ b/twitter_scrooge/twitter_scrooge.bzl @@ -113,7 +113,7 @@ def _gen_scrooge_srcjar_impl(ctx): for target in ctx.attr.remote_jars: remote_jars += _jar_filetype.filter(target.files) - # These are JARs that are declared externally and only have Thrift files + # These are JARs that are declared externally and only have Thrift files # in them. external_jars = _collect_external_jars(ctx.attr.deps)