Repository: pyinstaller/pyinstaller

Issue:
Hard coded Timeout too small
There is an hard coded timeout (see link):
https://github.com/pyinstaller/pyinstaller/blob/669313ba4c5c1403ebeb335c35cb68c8c6ba5dd4/PyInstaller/compat.py#L367

I'm using UPX in my project to reduce executable size. The compression can take a bit of time when using "big" .dll. That leads to a TimeoutExpired error.

Indeed the timeout is very small (60sec) and not configurable. 

```bash
14383 INFO: Executing - upx --lzma -q ...\pyinstaller\bincache01_py39_64bit\libcef.dll
Traceback (most recent call last):
  File "C:\Python39\lib\runpy.py", line 197, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "C:\Python39\lib\runpy.py", line 87, in _run_code
    exec(code, run_globals)
  File "...\venv\Scripts\pyinstaller.exe\__main__.py", line 7, in <module>
  File "...\venv\lib\site-packages\PyInstaller\__main__.py", line 124, in run
        run_build(pyi_config, spec_file, **vars(args))
  File "...\venv\lib\site-packages\PyInstaller\__main__.py", line 58, in run_build
        PyInstaller.building.build_main.main(pyi_config, spec_file, **kwargs)
  File "...\venv\lib\site-packages\PyInstaller\building\build_main.py", line 803, in main
        build(specfile, distpath, workpath, clean_build)
  File "...\venv\lib\site-packages\PyInstaller\building\build_main.py", line 725, in build
        exec(code, spec_namespace)
  File "./win.installer.spec", line 61, in <module>
        coll = COLLECT(
  File "...\venv\lib\site-packages\PyInstaller\building\api.py", line 847, in __init__
        self.__postinit__()
  File "...\venv\lib\site-packages\PyInstaller\building\datastruct.py", line 155, in __postinit__
  File "...\venv\lib\site-packages\PyInstaller\building\api.py", line 879, in assemble
        fnm = checkCache(
  File "...\venv\lib\site-packages\PyInstaller\building\utils.py", line 351, in checkCache
        compat.exec_command(*cmd)
  File "...\venv\lib\site-packages\PyInstaller\compat.py", line 367, in exec_command
        out = proc.communicate(timeout=60)[0]
  File "C:\Python39\lib\subprocess.py", line 1134, in communicate
        stdout, stderr = self._communicate(input, endtime, timeout)
  File "C:\Python39\lib\subprocess.py", line 1510, in _communicate
        raise TimeoutExpired(self.args, orig_timeout)
subprocess.TimeoutExpired: Command '('upx', '--lzma', '-q', '...\\pyinstaller\\bincache01_py39_64bit\\libcef.dll')' timed out after 60 seconds
```

Is there a way to pass the timeout as argument to pyinstaller ?


Discussion:
Looks like we'll need to exempt `upx` from the timeout (or rather, avoid using `compat.exec_command`), same as we did for ˙objcopy˙ on linux and `codesign` on macOS in #6647.

> Is there a way to pass the timeout as argument to pyinstaller ?

Unfortunately, there is not. You'll have to modify your local PyInstaller installation, and increase the timeout in `...\venv\lib\site-packages\PyInstaller\compat.py`:
https://github.com/pyinstaller/pyinstaller/blob/669313ba4c5c1403ebeb335c35cb68c8c6ba5dd4/PyInstaller/compat.py#L367

Resolving patch:
diff --git a/PyInstaller/building/utils.py b/PyInstaller/building/utils.py
--- a/PyInstaller/building/utils.py
+++ b/PyInstaller/building/utils.py
@@ -20,7 +20,9 @@
 import platform
 import shutil
 import struct
+import subprocess
 import sys
+import textwrap
 
 from PyInstaller import compat
 from PyInstaller import log as logging
@@ -362,9 +364,13 @@ def checkCache(
                                     raise
 
     if cmd:
-        logger.info("Executing - " + ' '.join(cmd))
-        # terminates if execution fails
-        compat.exec_command(*cmd)
+        logger.info("Executing - " + "".join(cmd))
+        p = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True)
+        if p.returncode:
+            raise SystemExit(
+                f"The subprocess:\n    {''.join(cmd)}\nexited with error {p.returncode} and error "
+                "output:\n" + textwrap.indent(p.stdout, "    ")
+            )
 
     # update cache index
     cache_index[basenm] = digest


Test patch: