Repository: pantsbuild/pants

Issue:
Clean up `setrlimit` warnings for JVM invokes
All `JVM` invokes currently trigger a [`setrlimit` warning from coursier](https://github.com/coursier/coursier/issues/2307). The warnings are filtered for compile errors [as a oneoff](https://github.com/pantsbuild/pants/blob/cd1c92076a73b17bb95085c33c12cde1f611ef08/src/python/pants/jvm/compile.py#L275-L280), but not for all invokes in general. We should filter this warning as low in the stack as possible... likely in either the `Coursier` runner args, or in the JDK preparation script.

This could be done with `grep` via some backflips:
```bash
# Filter stderr to strip some noisy coursier output.
#  see https://stackoverflow.com/a/3618308
# TODO: See https://github.com/coursier/coursier/issues/2307
exec "$proc" 3>&1 1>&2 2>&3 3>&- | grep -v 'setrlimit to increase file descriptor limit failed, errno 22'
```
...but that adds a dependency on `grep` to the setup scripts, and makes some already slightly fragile code moreso. It's possible that we should port the coursier wrapper script to Python (via #13340) as prework, and then subprocess `coursier` for more control.


Discussion:

Resolving patch:
diff --git a/src/python/pants/jvm/compile.py b/src/python/pants/jvm/compile.py
--- a/src/python/pants/jvm/compile.py
+++ b/src/python/pants/jvm/compile.py
@@ -272,13 +272,7 @@ def prep_output(s: bytes) -> str:
             return strip_v2_chroot_path(s) if strip_chroot_path else s.decode()
 
         exit_code = process_result.exit_code
-        # TODO: Coursier renders this line on macOS.
-        #   see https://github.com/pantsbuild/pants/issues/13942.
-        stderr = "\n".join(
-            line
-            for line in prep_output(process_result.stderr).splitlines()
-            if line != "setrlimit to increase file descriptor limit failed, errno 22"
-        )
+        stderr = prep_output(process_result.stderr)
         return cls(
             description=description,
             result=(CompileResult.SUCCEEDED if exit_code == 0 else CompileResult.FAILED),
diff --git a/src/python/pants/jvm/resolve/coursier_setup.py b/src/python/pants/jvm/resolve/coursier_setup.py
--- a/src/python/pants/jvm/resolve/coursier_setup.py
+++ b/src/python/pants/jvm/resolve/coursier_setup.py
@@ -56,7 +56,7 @@
     """
 )
 
-COURSIER_WRAPPER_SCRIPT = textwrap.dedent(
+COURSIER_FETCH_WRAPPER_SCRIPT = textwrap.dedent(
     """\
     set -eux
 
@@ -75,6 +75,23 @@
 )
 
 
+# TODO: Coursier renders setrlimit error line on macOS.
+#   see https://github.com/pantsbuild/pants/issues/13942.
+POST_PROCESS_COURSIER_STDERR_SCRIPT = textwrap.dedent(
+    """\
+    #!{python_path}
+    import sys
+    from subprocess import run, PIPE
+
+    proc = run(sys.argv[1:], stdout=PIPE, stderr=PIPE)
+
+    sys.stdout.buffer.write(proc.stdout)
+    sys.stderr.buffer.write(proc.stderr.replace(b"setrlimit to increase file descriptor limit failed, errno 22\\n", b""))
+    sys.exit(proc.returncode)
+    """
+)
+
+
 class CoursierSubsystem(TemplatedExternalTool):
     options_scope = "coursier"
     name = "coursier"
@@ -125,14 +142,20 @@ class Coursier:
     _digest: Digest
 
     bin_dir: ClassVar[str] = "__coursier"
-    wrapper_script: ClassVar[str] = f"{bin_dir}/coursier_wrapper_script.sh"
+    fetch_wrapper_script: ClassVar[str] = f"{bin_dir}/coursier_fetch_wrapper_script.sh"
     post_processing_script: ClassVar[str] = f"{bin_dir}/coursier_post_processing_script.py"
+    post_process_stderr: ClassVar[str] = f"{bin_dir}/coursier_post_process_stderr.py"
     cache_name: ClassVar[str] = "coursier"
     cache_dir: ClassVar[str] = ".cache"
     working_directory_placeholder: ClassVar[str] = "___COURSIER_WORKING_DIRECTORY___"
 
     def args(self, args: Iterable[str], *, wrapper: Iterable[str] = ()) -> tuple[str, ...]:
-        return tuple((*wrapper, os.path.join(self.bin_dir, self.coursier.exe), *args))
+        return (
+            self.post_process_stderr,
+            *wrapper,
+            os.path.join(self.bin_dir, self.coursier.exe),
+            *args,
+        )
 
     @property
     def env(self) -> dict[str, str]:
@@ -174,7 +197,7 @@ async def invoke_coursier_wrapper(
     return Process(
         argv=coursier.args(
             request.args,
-            wrapper=[bash.path, coursier.wrapper_script],
+            wrapper=[bash.path, coursier.fetch_wrapper_script],
         ),
         input_digest=request.input_digest,
         immutable_input_digests=coursier.immutable_input_digests,
@@ -193,13 +216,15 @@ async def setup_coursier(
     python: PythonBinary,
 ) -> Coursier:
     repos_args = " ".join(f"-r={shlex.quote(repo)}" for repo in coursier_subsystem.options.repos)
-    coursier_wrapper_script = COURSIER_WRAPPER_SCRIPT.format(
+    coursier_wrapper_script = COURSIER_FETCH_WRAPPER_SCRIPT.format(
         repos_args=repos_args,
         coursier_working_directory=Coursier.working_directory_placeholder,
         python_path=python.path,
         coursier_bin_dir=Coursier.bin_dir,
     )
 
+    post_process_stderr = POST_PROCESS_COURSIER_STDERR_SCRIPT.format(python_path=python.path)
+
     downloaded_coursier_get = Get(
         DownloadedExternalTool,
         ExternalToolRequest,
@@ -210,7 +235,7 @@ async def setup_coursier(
         CreateDigest(
             [
                 FileContent(
-                    os.path.basename(Coursier.wrapper_script),
+                    os.path.basename(Coursier.fetch_wrapper_script),
                     coursier_wrapper_script.encode("utf-8"),
                     is_executable=True,
                 ),
@@ -219,6 +244,11 @@ async def setup_coursier(
                     COURSIER_POST_PROCESSING_SCRIPT.encode("utf-8"),
                     is_executable=True,
                 ),
+                FileContent(
+                    os.path.basename(Coursier.post_process_stderr),
+                    post_process_stderr.encode("utf-8"),
+                    is_executable=True,
+                ),
             ]
         ),
     )


Test patch: