Repository: mesonbuild/meson

Issue:
Response file needed for Fortran project on Linux
Inspired by #7212.

A project with lots of Fortran source:

```sh
#!/bin/sh
set -eu
work_dir=meson_toolong
build_dir=b
n=12000
mkdir -p ${work_dir}
cd ${work_dir}
rm -rf ${build_dir}
{
  echo "project('toolong', 'fortran')"
  echo "srcs = ["
} > meson.build
for i in $(seq -w ${n})
do
  src_file=f${i}.f90
  {
    echo "Subroutine s${i}"
    echo "End"
  } > "${src_file}"
  echo " '${src_file}'," >> meson.build
done
{
  echo "]"
  echo "library('toolong', srcs)"
} >> meson.build
meson . ${build_dir}
cd ${build_dir}
ninja -v
```

gives

```
The Meson build system
Version: 0.59.0
Source dir: /home/mat_cross/documents/Meson/resp/meson_toolong
Build dir: /home/mat_cross/documents/Meson/resp/meson_toolong/b
Build type: native build
Project name: toolong
Project version: undefined
Fortran compiler for the host machine: gfortran (gcc 11.2.1 "GNU Fortran (GCC) 11.2.1 20210728 (Red Hat 11.2.1-1)")
Fortran linker for the host machine: gfortran ld.bfd 2.35.2-4
Host machine cpu family: x86_64
Host machine cpu: x86_64
Build targets in project: 1

Found ninja-1.10.2 at /usr/bin/ninja
ninja: fatal: posix_spawn: Argument list too long
```

(Python is 3.9.6).


Discussion:
Are you generating a static library, or a shared library?
> Are you generating a static library, or a shared library?

As per the Meson default via my shell script above (so, shared?)
It seems like it's falling over during ninja dependency generation.
For me it falls over trying to do the depscan step, which is a python program we maintain. But it does generate a rule for usin ghte fortran linker with a response file, so this actually an issue with the depscanner

Resolving patch:
diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py
--- a/mesonbuild/backend/ninjabackend.py
+++ b/mesonbuild/backend/ninjabackend.py
@@ -11,18 +11,20 @@
 # 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.
-import typing as T
+
+from collections import OrderedDict
+from enum import Enum, unique
+from functools import lru_cache
+from pathlib import PurePath, Path
+from textwrap import dedent
+import itertools
+import json
 import os
-import re
 import pickle
+import re
 import shlex
 import subprocess
-from collections import OrderedDict
-from enum import Enum, unique
-import itertools
-from textwrap import dedent
-from pathlib import PurePath, Path
-from functools import lru_cache
+import typing as T
 
 from . import backends
 from .. import modules
@@ -915,9 +917,16 @@ def generate_dependency_scan_target(self, target, compiled_sources, source2objec
         pickle_base = target.name + '.dat'
         pickle_file = os.path.join(self.get_target_private_dir(target), pickle_base).replace('\\', '/')
         pickle_abs = os.path.join(self.get_target_private_dir_abs(target), pickle_base).replace('\\', '/')
+        json_abs = os.path.join(self.get_target_private_dir_abs(target), f'{target.name}-deps.json').replace('\\', '/')
         rule_name = 'depscan'
         scan_sources = self.select_sources_to_scan(compiled_sources)
-        elem = NinjaBuildElement(self.all_outputs, depscan_file, rule_name, scan_sources)
+
+        # Dump the sources as a json list. This avoids potential probllems where
+        # the number of sources passed to depscan exceedes the limit imposed by
+        # the OS.
+        with open(json_abs, 'w', encoding='utf-8') as f:
+            json.dump(scan_sources, f)
+        elem = NinjaBuildElement(self.all_outputs, depscan_file, rule_name, json_abs)
         elem.add_item('picklefile', pickle_file)
         scaninfo = TargetDependencyScannerInfo(self.get_target_private_dir(target), source2object)
         with open(pickle_abs, 'wb') as p:
diff --git a/mesonbuild/scripts/depscan.py b/mesonbuild/scripts/depscan.py
--- a/mesonbuild/scripts/depscan.py
+++ b/mesonbuild/scripts/depscan.py
@@ -12,10 +12,11 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import json
+import os
 import pathlib
 import pickle
 import re
-import os
 import sys
 import typing as T
 
@@ -194,8 +195,9 @@ def scan(self) -> int:
         return 0
 
 def run(args: T.List[str]) -> int:
-    pickle_file = args[0]
-    outfile = args[1]
-    sources = args[2:]
+    assert len(args) == 3, 'got wrong number of arguments!'
+    pickle_file, outfile, jsonfile = args
+    with open(jsonfile, 'r', encoding='utf-8') as f:
+        sources = json.load(f)
     scanner = DependencyScanner(pickle_file, outfile, sources)
     return scanner.scan()


Test patch: