From 20c99d1d016be425f783b2747226aec9ef574e2b Mon Sep 17 00:00:00 2001 From: Nadav Samet Date: Wed, 8 May 2024 19:18:06 -0700 Subject: [PATCH] Add a method to run protoc with parameters given in a file. --- .../scala/protocbridge/ProtocRunner.scala | 28 +++++++++++++++++++ .../protocbridge/ProtocIntegrationSpec.scala | 12 ++++++-- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/bridge/src/main/scala/protocbridge/ProtocRunner.scala b/bridge/src/main/scala/protocbridge/ProtocRunner.scala index 771abd2..b665ad7 100644 --- a/bridge/src/main/scala/protocbridge/ProtocRunner.scala +++ b/bridge/src/main/scala/protocbridge/ProtocRunner.scala @@ -1,5 +1,6 @@ package protocbridge +import java.nio.file.Files import scala.io.Source import scala.sys.process.Process import scala.sys.process.ProcessLogger @@ -74,4 +75,31 @@ object ProtocRunner { extraEnv: _* ).! } + + // Transforms the given protoc runner to a new runner that writes the + // options into a temporary file and passes the file to `protoc` as an `@` + // parameter. + def withParametersAsFile[T](underlying: ProtocRunner[T]): ProtocRunner[T] = + fromFunction { (args, extraEnv) => + { + val argumentFile = Files.createTempFile("protoc-args-", ".txt") + try { + val writer = Files.newBufferedWriter(argumentFile) + + try { + args.foreach { arg => + writer.write(arg) + writer.write('\n') + } + } finally { + writer.close() + } + + val fileArgument = s"@${argumentFile.toString}" + underlying.run(Seq(fileArgument), extraEnv) + } finally { + Files.delete(argumentFile) + } + } + } } diff --git a/bridge/src/test/scala/protocbridge/ProtocIntegrationSpec.scala b/bridge/src/test/scala/protocbridge/ProtocIntegrationSpec.scala index cdb6650..606457a 100644 --- a/bridge/src/test/scala/protocbridge/ProtocIntegrationSpec.scala +++ b/bridge/src/test/scala/protocbridge/ProtocIntegrationSpec.scala @@ -67,7 +67,7 @@ object TestUtils { } class ProtocIntegrationSpec extends AnyFlatSpec with Matchers { - "ProtocBridge.run" should "invoke JVM and Java plugin properly" in { + def invokeProtocProperly(runner: ProtocRunner[Int]) = { val protoFile = new File(getClass.getResource("/test.proto").getFile).getAbsolutePath val protoDir = new File(getClass.getResource("/").getFile).getAbsolutePath @@ -77,7 +77,7 @@ class ProtocIntegrationSpec extends AnyFlatSpec with Matchers { (0 to 4).map(i => Files.createTempDirectory(s"testout$i").toFile()) ProtocBridge.execute( - RunProtoc, + runner, Seq( protocbridge.gens.java("3.8.0") -> javaOutDir, TestJvmPlugin -> testOutDirs(0), @@ -111,6 +111,14 @@ class ProtocIntegrationSpec extends AnyFlatSpec with Matchers { readLines(new File(testOutDirs(0), "parameters.txt")) must be(Seq("Empty")) } + "ProtocBridge.run" should "invoke JVM and Java plugin properly" in { + invokeProtocProperly(RunProtoc) + } + + "ProtocBridge.run" should "invoke JVM and Java plugin properly with options file" in { + invokeProtocProperly(ProtocRunner.withParametersAsFile(RunProtoc)) + } + it should "not deadlock for highly concurrent invocations" in { val availableProcessors = Runtime.getRuntime.availableProcessors assert(