From 5c3036e57a50a4af810985c27a1e558a76b69201 Mon Sep 17 00:00:00 2001
From: Pieter Bos
Date: Mon, 22 Jan 2024 13:22:18 +0100
Subject: [PATCH] windows: write arguments to protoc to a file
---
.../scala/protocbridge/ProtocRunner.scala | 46 ++++++++++++++++---
.../protocbridge/ProtocCacheCoursier.scala | 8 ++--
2 files changed, 45 insertions(+), 9 deletions(-)
diff --git a/bridge/src/main/scala/protocbridge/ProtocRunner.scala b/bridge/src/main/scala/protocbridge/ProtocRunner.scala
index 771abd2..2000f22 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
@@ -57,6 +58,37 @@ object ProtocRunner {
None
}
+ def maybeBoxArgsInFile[T](args: Seq[String])(withArgs: Seq[String] => T): T =
+ detectedOs match {
+ case "windows" =>
+ // The default command line length limit is 32767, which we might exceed.
+ // See also https://devblogs.microsoft.com/oldnewthing/20031210-00/?p=41553
+ // As of protobuf v3.5.0, you can pass arguments via a file instead with @.
+ // Arguments in the file are delimited by a newline and not escaped in any way.
+ val argumentFile = Files.createTempFile("scalapb-arguments-", ".txt")
+
+ try {
+ val writer = Files.newBufferedWriter(argumentFile)
+
+ try {
+ for (arg <- args) {
+ writer.write(arg)
+ writer.write("\n")
+ }
+ } finally {
+ writer.close()
+ }
+
+ val fileArgument = s"@${argumentFile.toString}"
+ withArgs(Seq(fileArgument))
+ } finally {
+ Files.delete(argumentFile)
+ }
+ case _ =>
+ // No special handling: just use the arguments as-is.
+ withArgs(args)
+ }
+
// This version of maybeNixDynamicLinker() finds ld-linux and also uses it
// to verify that the executable is dynamic. Newer version (>=3.23.0) of
// protoc are static, and thus do not load with ld-linux.
@@ -67,11 +99,13 @@ object ProtocRunner {
def apply(executable: String): ProtocRunner[Int] = ProtocRunner.fromFunction {
case (args, extraEnv) =>
- Process(
- command =
- (maybeNixDynamicLinker(executable).toSeq :+ executable) ++ args,
- cwd = None,
- extraEnv: _*
- ).!
+ maybeBoxArgsInFile(args) { args =>
+ Process(
+ command =
+ (maybeNixDynamicLinker(executable).toSeq :+ executable) ++ args,
+ cwd = None,
+ extraEnv: _*
+ ).!
+ }
}
}
diff --git a/protoc-cache-coursier/src/main/scala/protocbridge/ProtocCacheCoursier.scala b/protoc-cache-coursier/src/main/scala/protocbridge/ProtocCacheCoursier.scala
index 3ee8ad0..efbd42d 100644
--- a/protoc-cache-coursier/src/main/scala/protocbridge/ProtocCacheCoursier.scala
+++ b/protoc-cache-coursier/src/main/scala/protocbridge/ProtocCacheCoursier.scala
@@ -24,9 +24,11 @@ object CoursierProtocCache {
val protoc = getProtoc(version).getAbsolutePath()
- val cmd =
- (ProtocRunner.maybeNixDynamicLinker(protoc).toSeq :+ protoc) ++ args
- Process(command = cmd, cwd = None, extraEnv: _*).!
+ ProtocRunner.maybeBoxArgsInFile(args) { args =>
+ val cmd =
+ (ProtocRunner.maybeNixDynamicLinker(protoc).toSeq :+ protoc) ++ args
+ Process(command = cmd, cwd = None, extraEnv: _*).!
+ }
}
private[this] def download(tmpDir: File, dep: Dependency): Future[File] = {