Repository: bitcoin/bitcoin (language: cpp)

Issue:
gen-manpages.py should skip nonexistent binaries and still work for the existent binaries
### Is there an existing issue for this?

- [X] I have searched the existing issues

### Current behaviour

By executing `BUILDDIR=$PWD/build contrib/devtools/gen-manpages.py`, if one binary is not found, it will stop the generation of all manpages.

For example: Executing gen-manpages.py without bitcoin-qt. The command exits after detecting that bitcoin-qt does not exist, and no manpage is generated, even though only bitcoin-qt is missing.

```bash
➜  bitcoin git:(master) ✗ BUILDDIR=$PWD/build contrib/devtools/gen-manpages.py
/home/andre/repos/bitcoin/build/src/qt/bitcoin-qt not found or not an executable
➜  bitcoin git:(master) ✗ 
```

Initially, I thought this behavior was intended, but after investigating the older shell script implementation `gen-manpages.sh`, I realized that’s not the case. `gen-manpages.sh` was able to generate all the manpages and just warned the user about missing binaries.

Example using the last version of the shell script: https://github.com/bitcoin/bitcoin/blob/a5edd191be93aff8f9c0f60f04e711e2e78ecc77/contrib/devtools/gen-manpages.sh

```bash
➜  bitcoin git:(master) ✗ BUILDDIR=$PWD/build contrib/devtools/gen-manpages.sh
contrib/devtools/gen-manpages.sh: line 25: /home/andre/repos/bitcoin/build/src/qt/bitcoin-qt: No such file or directory
help2man: can't get `--help' info from /home/andre/repos/bitcoin/build/src/qt/bitcoin-qt
Try `--no-discard-stderr' if option outputs to stderr
➜  bitcoin git:(master) ✗ git status
On branch master
Your branch is up to date with 'origin/master'.

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
	modified:   doc/man/bitcoin-cli.1
	modified:   doc/man/bitcoin-tx.1
	modified:   doc/man/bitcoin-util.1
	modified:   doc/man/bitcoin-wallet.1
	modified:   doc/man/bitcoind.1

```

The script warned about `bitcoin-qt` being missing but still generated the other manpages, as you can see with the git status command.

### Expected behaviour

```sh
➜  bitcoin git:(master) ✗ BUILDDIR=$PWD/build contrib/devtools/gen-manpages.py
/home/andre/repos/bitcoin/build/src/qt/bitcoin-qt not found or not an executable. Skipping...
Generating /home/andre/repos/bitcoin/doc/man/bitcoind.1…
Generating /home/andre/repos/bitcoin/doc/man/bitcoin-cli.1…
Generating /home/andre/repos/bitcoin/doc/man/bitcoin-tx.1…
Generating /home/andre/repos/bitcoin/doc/man/bitcoin-wallet.1…
Generating /home/andre/repos/bitcoin/doc/man/bitcoin-util.1…
```

### Steps to reproduce

Run `BUILDDIR=$PWD/build contrib/devtools/gen-manpages.py` without having one of the binaries compiled e.g. bitcoin-qt.

### Relevant log output

_No response_

### How did you obtain Bitcoin Core

Compiled from source

### What version of Bitcoin Core are you using?

master

### Operating system and version

Debian

### Machine specifications

_No response_


Discussion / hints:

Fix patch:
diff --git a/contrib/devtools/gen-manpages.py b/contrib/devtools/gen-manpages.py
index 92acd9a40373c..14c8c408e83af 100755
--- a/contrib/devtools/gen-manpages.py
+++ b/contrib/devtools/gen-manpages.py
@@ -6,6 +6,7 @@
 import subprocess
 import sys
 import tempfile
+import argparse
 
 BINARIES = [
 'src/bitcoind',
@@ -16,6 +17,18 @@
 'src/qt/bitcoin-qt',
 ]
 
+parser = argparse.ArgumentParser(
+    formatter_class=argparse.RawDescriptionHelpFormatter,
+)
+parser.add_argument(
+    "-s",
+    "--skip-missing-binaries",
+    action="store_true",
+    default=False,
+    help="skip generation for binaries that are not found in the build path",
+)
+args = parser.parse_args()
+
 # Paths to external utilities.
 git = os.getenv('GIT', 'git')
 help2man = os.getenv('HELP2MAN', 'help2man')
@@ -38,8 +51,12 @@
     try:
         r = subprocess.run([abspath, "--version"], stdout=subprocess.PIPE, check=True, text=True)
     except IOError:
-        print(f'{abspath} not found or not an executable', file=sys.stderr)
-        sys.exit(1)
+        if(args.skip_missing_binaries):
+            print(f'{abspath} not found or not an executable. Skipping...', file=sys.stderr)
+            continue
+        else:
+            print(f'{abspath} not found or not an executable', file=sys.stderr)
+            sys.exit(1)
     # take first line (which must contain version)
     verstr = r.stdout.splitlines()[0]
     # last word of line is the actual version e.g. v22.99.0-5c6b3d5b3508
@@ -51,6 +68,10 @@
 
     versions.append((abspath, verstr, copyright))
 
+if not versions:
+    print(f'No binaries found in {builddir}. Please ensure the binaries are present in {builddir}, or set another build path using the BUILDDIR env variable.')
+    sys.exit(1)
+
 if any(verstr.endswith('-dirty') for (_, verstr, _) in versions):
     print("WARNING: Binaries were built from a dirty tree.")
     print('man pages generated from dirty binaries should NOT be committed.')


Test patch: